expo-updates 1.0.0-canary-20241021-7aba813 → 1.0.0-canary-20250122-166c2cb

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 (106) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/android/build.gradle +3 -2
  3. package/android/proguard-rules.pro +1 -1
  4. package/android/src/main/java/expo/modules/updates/UpdatesController.kt +5 -5
  5. package/android/src/main/java/expo/modules/updates/UpdatesModule.kt +22 -9
  6. package/android/src/main/java/expo/modules/updates/errorrecovery/ErrorRecovery.kt +5 -5
  7. package/android/src/main/java/expo/modules/updates/events/IUpdatesEventManager.kt +4 -4
  8. package/android/src/main/java/expo/modules/updates/events/NoOpUpdatesEventManager.kt +2 -2
  9. package/android/src/main/java/expo/modules/updates/events/UpdatesEventManager.kt +8 -12
  10. package/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt +25 -24
  11. package/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt +70 -58
  12. package/android/src/main/java/expo/modules/updates/procedures/RecreateReactContextProcedure.kt +1 -1
  13. package/android/src/main/java/expo/modules/updates/procedures/RelaunchProcedure.kt +3 -3
  14. package/android/src/main/java/expo/modules/updates/procedures/RestartReactAppExtensions.kt +2 -2
  15. package/android/src/main/java/expo/modules/updates/procedures/StartupProcedure.kt +2 -0
  16. package/android/src/main/java/expo/modules/updates/procedures/StateMachineProcedure.kt +3 -2
  17. package/android/src/main/java/expo/modules/updates/procedures/StateMachineSerialExecutorQueue.kt +2 -2
  18. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateContext.kt +20 -15
  19. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateEvent.kt +9 -7
  20. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateEventType.kt +2 -0
  21. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateMachine.kt +13 -5
  22. package/build/ExpoUpdates.web.d.ts +33 -2
  23. package/build/ExpoUpdates.web.d.ts.map +1 -1
  24. package/build/ExpoUpdates.web.js +50 -3
  25. package/build/ExpoUpdates.web.js.map +1 -1
  26. package/build/ExpoUpdatesModule.types.d.ts +48 -5
  27. package/build/ExpoUpdatesModule.types.d.ts.map +1 -1
  28. package/build/ExpoUpdatesModule.types.js.map +1 -1
  29. package/build/Updates.d.ts.map +1 -1
  30. package/build/Updates.js +5 -4
  31. package/build/Updates.js.map +1 -1
  32. package/build/Updates.types.d.ts +2 -8
  33. package/build/Updates.types.d.ts.map +1 -1
  34. package/build/Updates.types.js.map +1 -1
  35. package/build/UseUpdates.d.ts.map +1 -1
  36. package/build/UseUpdates.js +1 -1
  37. package/build/UseUpdates.js.map +1 -1
  38. package/build/UseUpdates.types.d.ts +14 -5
  39. package/build/UseUpdates.types.d.ts.map +1 -1
  40. package/build/UseUpdates.types.js.map +1 -1
  41. package/build/UseUpdatesUtils.d.ts +2 -14
  42. package/build/UseUpdatesUtils.d.ts.map +1 -1
  43. package/build/UseUpdatesUtils.js +4 -1
  44. package/build/UseUpdatesUtils.js.map +1 -1
  45. package/cli/build/syncConfigurationToNativeAsync.js +1 -0
  46. package/cli/src/syncConfigurationToNativeAsync.ts +1 -0
  47. package/e2e/fixtures/App.tsx +18 -0
  48. package/e2e/fixtures/Updates-error-recovery.e2e.ts +2 -2
  49. package/e2e/fixtures/Updates-fingerprint.e2e.ts +0 -38
  50. package/e2e/fixtures/Updates-startup.e2e.ts +18 -3
  51. package/e2e/fixtures/Updates.e2e.ts +33 -44
  52. package/e2e/fixtures/project_files/.fingerprintignore +3 -1
  53. package/e2e/setup/project.ts +8 -3
  54. package/e2e-cli/jest.config.js +8 -0
  55. package/expo-updates-gradle-plugin/build.gradle.kts +1 -1
  56. package/ios/EXUpdates/AppController.swift +10 -8
  57. package/ios/EXUpdates/AppLauncher/AppLauncherWithDatabase.swift +3 -3
  58. package/ios/EXUpdates/AppLoader/AppLoaderTask.swift +9 -11
  59. package/ios/EXUpdates/AppLoader/FileDownloader.swift +4 -4
  60. package/ios/EXUpdates/AppLoader/RemoteAppLoader.swift +1 -1
  61. package/ios/EXUpdates/Database/UpdatesDatabase.swift +2 -2
  62. package/ios/EXUpdates/Database/UpdatesDatabaseInitialization.swift +14 -6
  63. package/ios/EXUpdates/Database/UpdatesReaper.swift +4 -3
  64. package/ios/EXUpdates/DevLauncherAppController.swift +4 -2
  65. package/ios/EXUpdates/DisabledAppController.swift +5 -1
  66. package/ios/EXUpdates/EnabledAppController.swift +1 -44
  67. package/ios/EXUpdates/ErrorRecovery.swift +4 -2
  68. package/ios/EXUpdates/Events/NoOpUpdatesEventManager.swift +1 -1
  69. package/ios/EXUpdates/Events/QueueUpdatesEventManager.swift +6 -12
  70. package/ios/EXUpdates/Events/UpdatesEventManager.swift +5 -1
  71. package/ios/EXUpdates/Logging/UpdatesLogger.swift +4 -2
  72. package/ios/EXUpdates/Procedures/CheckForUpdateProcedure.swift +11 -13
  73. package/ios/EXUpdates/Procedures/FetchUpdateProcedure.swift +6 -6
  74. package/ios/EXUpdates/Procedures/RecreateReactContextProcedure.swift +2 -2
  75. package/ios/EXUpdates/Procedures/RelaunchProcedure.swift +6 -4
  76. package/ios/EXUpdates/Procedures/StartupProcedure.swift +16 -14
  77. package/ios/EXUpdates/Procedures/StateMachineProcedure.swift +9 -9
  78. package/ios/EXUpdates/Procedures/StateMachineSerialExecutorQueue.swift +2 -2
  79. package/ios/EXUpdates/ReactDelegateHandler/ExpoUpdatesReactDelegateHandler.swift +32 -0
  80. package/ios/EXUpdates/UpdatesModule.swift +15 -5
  81. package/ios/EXUpdates/UpdatesStateMachine.swift +126 -156
  82. package/ios/Tests/AppLauncherWithDatabaseSpec.swift +3 -2
  83. package/ios/Tests/DatabaseInitializationSpec.swift +37 -16
  84. package/ios/Tests/DatabaseIntegrityCheckSpec.swift +1 -1
  85. package/ios/Tests/ErrorRecoverySpec.swift +1 -1
  86. package/ios/Tests/FileDownloaderManifestParsingSpec.swift +24 -24
  87. package/ios/Tests/FileDownloaderSpec.swift +9 -9
  88. package/ios/Tests/UpdatesBuildDataSpec.swift +1 -1
  89. package/ios/Tests/UpdatesDatabaseSpec.swift +1 -1
  90. package/ios/Tests/UpdatesLoggerSpec.swift +29 -9
  91. package/ios/Tests/UpdatesStateMachineSpec.swift +71 -34
  92. package/package.json +13 -14
  93. package/src/ExpoUpdates.web.ts +78 -3
  94. package/src/ExpoUpdatesModule.types.ts +61 -4
  95. package/src/Updates.ts +9 -5
  96. package/src/Updates.types.ts +2 -10
  97. package/src/UseUpdates.ts +1 -2
  98. package/src/UseUpdates.types.ts +14 -5
  99. package/src/UseUpdatesUtils.ts +10 -17
  100. package/utils/build/createFingerprintAsync.d.ts +1 -1
  101. package/utils/build/createFingerprintAsync.js +1 -1
  102. package/utils/build/resolveRuntimeVersionAsync.d.ts +1 -1
  103. package/utils/src/createFingerprintAsync.ts +1 -1
  104. package/utils/src/resolveRuntimeVersionAsync.ts +1 -1
  105. package/fingerprint.d.ts +0 -1
  106. package/fingerprint.js +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,89 @@
4
4
 
5
5
  ### 🛠 Breaking changes
6
6
 
7
+ - Remove a few long-deprecated typescript types. ([#34215](https://github.com/expo/expo/pull/34215) by [@wschurman](https://github.com/wschurman))
8
+
9
+ ### 🎉 New features
10
+
11
+ - Add new state machine context about startup procedure. ([#32433](https://github.com/expo/expo/pull/32433) by [@wschurman](https://github.com/wschurman))
12
+
13
+ ### 🐛 Bug fixes
14
+
15
+ - Fix an issue where `launchFallbackUpdateFromDisk` is called from a foreground thread leading to ANRs. ([#33693](https://github.com/expo/expo/pull/33693) by [@alanjhughes](https://github.com/alanjhughes))
16
+ - [android] Use more robust mechanism for determining empty multipart bodies. ([#33977](https://github.com/expo/expo/pull/33977) by [@wschurman](https://github.com/wschurman))
17
+
18
+ ### 💡 Others
19
+
20
+ - [iOS] Inject logger from controllers down to dependent objects. ([#34035](https://github.com/expo/expo/pull/34035) by [@wschurman](https://github.com/wschurman))
21
+
22
+ ## 0.26.10 - 2024-12-05
23
+
24
+ ### 🐛 Bug fixes
25
+
26
+ - Fix black screen appearing instead of the splashscreen on launch. ([#33432](https://github.com/expo/expo/pull/33432) by [@alanjhughes](https://github.com/alanjhughes))
27
+
28
+ ## 0.26.9 — 2024-11-22
29
+
30
+ ### 🐛 Bug fixes
31
+
32
+ - Fixed `Inconsistent JVM-target compatibility` error when building with JDK 21. ([#33148](https://github.com/expo/expo/pull/33148) by [@kudo](https://github.com/kudo))
33
+
34
+ ### 💡 Others
35
+
36
+ - Introduced `ReactNativeFeatureFlags` compat to fix React Native 0.77 breaking changes. ([#33077](https://github.com/expo/expo/pull/33077) by [@kudo](https://github.com/kudo))
37
+
38
+ ## 0.26.8 — 2024-11-20
39
+
40
+ _This version does not introduce any user-facing changes._
41
+
42
+ ## 0.26.7 — 2024-11-14
43
+
44
+ _This version does not introduce any user-facing changes._
45
+
46
+ ## 0.26.6 — 2024-11-13
47
+
48
+ ### 💡 Others
49
+
50
+ - Upgrade `express` to `4.21.1` for `send`. ([#32852](https://github.com/expo/expo/pull/32852) by [@MWein](https://github.com/MWein))
51
+
52
+ ## 0.26.5 — 2024-11-07
53
+
54
+ _This version does not introduce any user-facing changes._
55
+
56
+ ## 0.26.4 — 2024-11-04
57
+
58
+ ### 🐛 Bug fixes
59
+
60
+ - Improve web native module interface completeness ([#32512](https://github.com/expo/expo/pull/32512) by [@wschurman](https://github.com/wschurman))
61
+
62
+ ## 0.26.3 — 2024-10-31
63
+
64
+ _This version does not introduce any user-facing changes._
65
+
66
+ ## 0.26.2 — 2024-10-24
67
+
68
+ ### 🐛 Bug fixes
69
+
70
+ - Move event emitting responsibility to module. ([#32248](https://github.com/expo/expo/pull/32248) by [@wschurman](https://github.com/wschurman))
71
+
72
+ ### 💡 Others
73
+
74
+ - Use enum event in OnStartObserving and OnStopObserving. ([#32252](https://github.com/expo/expo/pull/32252) by [@wschurman](https://github.com/wschurman))
75
+
76
+ ## 0.26.1 — 2024-10-22
77
+
78
+ ### 🐛 Bug fixes
79
+
80
+ - Fixed Android launch crash when R8 is enabled. ([#32226](https://github.com/expo/expo/pull/32226) by [@kudo](https://github.com/kudo))
81
+
82
+ ### 💡 Others
83
+
84
+ - Fixed updates E2E tests. ([#32226](https://github.com/expo/expo/pull/32226) by [@kudo](https://github.com/kudo))
85
+
86
+ ## 0.26.0 — 2024-10-22
87
+
88
+ ### 🛠 Breaking changes
89
+
7
90
  - Bumped iOS and tvOS deployment target to 15.1. ([#30840](https://github.com/expo/expo/pull/30840) by [@tsapeta](https://github.com/tsapeta))
8
91
 
9
92
  ### 🎉 New features
@@ -16,11 +16,12 @@ apply plugin: 'com.android.library'
16
16
  apply plugin: 'com.google.devtools.ksp'
17
17
 
18
18
  group = 'host.exp.exponent'
19
- version = '0.25.14'
19
+ version = '0.26.9'
20
20
 
21
21
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
22
22
  apply from: expoModulesCorePlugin
23
23
  applyKotlinExpoModulesCorePlugin()
24
+ applyKspJvmToolchain()
24
25
  useCoreDependencies()
25
26
  useDefaultAndroidSdkVersions()
26
27
  useExpoPublishing()
@@ -62,7 +63,7 @@ android {
62
63
  namespace "expo.modules.updates"
63
64
  defaultConfig {
64
65
  versionCode 31
65
- versionName '0.25.14'
66
+ versionName '0.26.9'
66
67
  consumerProguardFiles("proguard-rules.pro")
67
68
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
68
69
 
@@ -3,5 +3,5 @@
3
3
  }
4
4
 
5
5
  -keepclassmembers class com.facebook.react.devsupport.ReleaseDevSupportManager {
6
- private final com.facebook.react.bridge.DefaultJSExceptionHandler mDefaultJSExceptionHandler;
6
+ private final com.facebook.react.bridge.DefaultJSExceptionHandler defaultJSExceptionHandler;
7
7
  }
@@ -2,7 +2,7 @@ package expo.modules.updates
2
2
 
3
3
  import android.content.Context
4
4
  import com.facebook.react.ReactApplication
5
- import expo.modules.kotlin.events.EventEmitter
5
+ import expo.modules.updates.events.IUpdatesEventManagerObserver
6
6
  import expo.modules.updates.loader.LoaderTask
7
7
  import expo.modules.updates.logging.UpdatesErrorCode
8
8
  import expo.modules.updates.logging.UpdatesLogger
@@ -151,13 +151,13 @@ class UpdatesController {
151
151
  }
152
152
  }
153
153
 
154
- internal fun onEventListenerStartObserving(eventEmitter: EventEmitter?) {
155
- singletonInstance?.eventManager?.eventEmitter = eventEmitter
154
+ internal fun setUpdatesEventManagerObserver(observer: WeakReference<IUpdatesEventManagerObserver>) {
155
+ singletonInstance?.eventManager?.observer = observer
156
156
  singletonInstance?.onEventListenerStartObserving()
157
157
  }
158
158
 
159
- internal fun onEventListenerStopObserving() {
160
- singletonInstance?.eventManager?.eventEmitter = null
159
+ internal fun removeUpdatesEventManagerObserver() {
160
+ singletonInstance?.eventManager?.observer = null
161
161
  }
162
162
  }
163
163
  }
@@ -8,19 +8,26 @@ import expo.modules.kotlin.exception.CodedException
8
8
  import expo.modules.kotlin.exception.Exceptions
9
9
  import expo.modules.kotlin.modules.Module
10
10
  import expo.modules.kotlin.modules.ModuleDefinition
11
- import expo.modules.updates.events.UpdatesJSEvent
11
+ import expo.modules.kotlin.types.Enumerable
12
+ import expo.modules.updates.events.IUpdatesEventManagerObserver
12
13
  import expo.modules.updates.logging.UpdatesErrorCode
13
14
  import expo.modules.updates.logging.UpdatesLogEntry
14
15
  import expo.modules.updates.logging.UpdatesLogReader
15
16
  import expo.modules.updates.logging.UpdatesLogger
17
+ import expo.modules.updates.statemachine.UpdatesStateContext
18
+ import java.lang.ref.WeakReference
16
19
  import java.util.Date
17
20
 
21
+ enum class UpdatesJSEvent(val eventName: String) : Enumerable {
22
+ StateChange("Expo.nativeUpdatesStateChangeEvent")
23
+ }
24
+
18
25
  /**
19
26
  * Exported module which provides to the JS runtime information about the currently running update
20
27
  * and updates state, along with methods to check for and download new updates, reload with the
21
28
  * newest downloaded update applied, and read/clear native log entries.
22
29
  */
23
- class UpdatesModule : Module() {
30
+ class UpdatesModule : Module(), IUpdatesEventManagerObserver {
24
31
  private val logger: UpdatesLogger
25
32
  get() = UpdatesLogger(context)
26
33
 
@@ -30,21 +37,23 @@ class UpdatesModule : Module() {
30
37
  override fun definition() = ModuleDefinition {
31
38
  Name("ExpoUpdates")
32
39
 
33
- Events(
34
- UpdatesJSEvent.StateChange.eventName
35
- )
40
+ Events<UpdatesJSEvent>()
36
41
 
37
42
  Constants {
38
43
  UpdatesLogger(context).info("UpdatesModule: getConstants called", UpdatesErrorCode.None)
39
44
  UpdatesController.instance.getConstantsForModule().toModuleConstantsMap()
40
45
  }
41
46
 
42
- OnStartObserving {
43
- UpdatesController.onEventListenerStartObserving(appContext.eventEmitter(this@UpdatesModule))
47
+ OnStartObserving(UpdatesJSEvent.StateChange) {
48
+ UpdatesController.setUpdatesEventManagerObserver(WeakReference(this@UpdatesModule))
49
+ }
50
+
51
+ OnStopObserving(UpdatesJSEvent.StateChange) {
52
+ UpdatesController.removeUpdatesEventManagerObserver()
44
53
  }
45
54
 
46
- OnStopObserving {
47
- UpdatesController.onEventListenerStopObserving()
55
+ OnDestroy {
56
+ UpdatesController.removeUpdatesEventManagerObserver()
48
57
  }
49
58
 
50
59
  AsyncFunction("reload") { promise: Promise ->
@@ -240,4 +249,8 @@ class UpdatesModule : Module() {
240
249
  )
241
250
  }
242
251
  }
252
+
253
+ override fun onStateMachineContextEvent(context: UpdatesStateContext) {
254
+ sendEvent(UpdatesJSEvent.StateChange, Bundle().apply { putBundle("context", context.bundle) })
255
+ }
243
256
  }
@@ -6,9 +6,9 @@ import com.facebook.react.bridge.DefaultJSExceptionHandler
6
6
  import com.facebook.react.bridge.ReactMarker
7
7
  import com.facebook.react.bridge.ReactMarker.MarkerListener
8
8
  import com.facebook.react.bridge.ReactMarkerConstants
9
- import com.facebook.react.config.ReactFeatureFlags
10
9
  import com.facebook.react.devsupport.ReleaseDevSupportManager
11
10
  import com.facebook.react.devsupport.interfaces.DevSupportManager
11
+ import expo.modules.rncompatibility.ReactNativeFeatureFlags
12
12
  import expo.modules.updates.logging.UpdatesErrorCode
13
13
  import expo.modules.updates.logging.UpdatesLogger
14
14
  import java.lang.ref.WeakReference
@@ -97,7 +97,7 @@ class ErrorRecovery(
97
97
  }
98
98
 
99
99
  private fun registerErrorHandler(devSupportManager: DevSupportManager) {
100
- if (ReactFeatureFlags.enableBridgelessArchitecture) {
100
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
101
101
  registerErrorHandlerImplBridgeless()
102
102
  } else {
103
103
  registerErrorHandlerImplBridge(devSupportManager)
@@ -120,7 +120,7 @@ class ErrorRecovery(
120
120
  }
121
121
  }
122
122
  val devSupportManagerClass = devSupportManager.javaClass
123
- previousExceptionHandler = devSupportManagerClass.getDeclaredField("mDefaultJSExceptionHandler").let { field ->
123
+ previousExceptionHandler = devSupportManagerClass.getDeclaredField("defaultJSExceptionHandler").let { field ->
124
124
  field.isAccessible = true
125
125
  val previousValue = field[devSupportManager]
126
126
  field[devSupportManager] = defaultJSExceptionHandler
@@ -130,7 +130,7 @@ class ErrorRecovery(
130
130
  }
131
131
 
132
132
  private fun unregisterErrorHandler() {
133
- if (ReactFeatureFlags.enableBridgelessArchitecture) {
133
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
134
134
  unregisterErrorHandlerImplBridgeless()
135
135
  } else {
136
136
  unregisterErrorHandlerImplBridge()
@@ -152,7 +152,7 @@ class ErrorRecovery(
152
152
  }
153
153
 
154
154
  val devSupportManagerClass = devSupportManager.javaClass
155
- devSupportManagerClass.getDeclaredField("mDefaultJSExceptionHandler").let { field ->
155
+ devSupportManagerClass.getDeclaredField("defaultJSExceptionHandler").let { field ->
156
156
  field.isAccessible = true
157
157
  field[devSupportManager] = previousExceptionHandler
158
158
  }
@@ -1,13 +1,13 @@
1
1
  package expo.modules.updates.events
2
2
 
3
- import expo.modules.kotlin.events.EventEmitter
4
3
  import expo.modules.updates.statemachine.UpdatesStateContext
4
+ import java.lang.ref.WeakReference
5
5
 
6
- enum class UpdatesJSEvent(val eventName: String) {
7
- StateChange("Expo.nativeUpdatesStateChangeEvent")
6
+ interface IUpdatesEventManagerObserver {
7
+ fun onStateMachineContextEvent(context: UpdatesStateContext)
8
8
  }
9
9
 
10
10
  interface IUpdatesEventManager {
11
- var eventEmitter: EventEmitter?
11
+ var observer: WeakReference<IUpdatesEventManagerObserver>?
12
12
  fun sendStateMachineContextEvent(context: UpdatesStateContext)
13
13
  }
@@ -1,9 +1,9 @@
1
1
  package expo.modules.updates.events
2
2
 
3
- import expo.modules.kotlin.events.EventEmitter
4
3
  import expo.modules.updates.statemachine.UpdatesStateContext
4
+ import java.lang.ref.WeakReference
5
5
 
6
6
  class NoOpUpdatesEventManager : IUpdatesEventManager {
7
- override var eventEmitter: EventEmitter? = null
7
+ override var observer: WeakReference<IUpdatesEventManagerObserver>? = null
8
8
  override fun sendStateMachineContextEvent(context: UpdatesStateContext) {}
9
9
  }
@@ -1,31 +1,27 @@
1
1
  package expo.modules.updates.events
2
2
 
3
- import expo.modules.kotlin.events.EventEmitter
4
3
  import expo.modules.updates.logging.UpdatesErrorCode
5
4
  import expo.modules.updates.logging.UpdatesLogger
6
5
  import expo.modules.updates.statemachine.UpdatesStateContext
6
+ import java.lang.ref.WeakReference
7
7
 
8
8
  class UpdatesEventManager(private val logger: UpdatesLogger) : IUpdatesEventManager {
9
- override var eventEmitter: EventEmitter? = null
9
+ override var observer: WeakReference<IUpdatesEventManagerObserver>? = null
10
10
 
11
11
  override fun sendStateMachineContextEvent(context: UpdatesStateContext) {
12
- val eventName = UpdatesJSEvent.StateChange.eventName
12
+ logger.debug("Sending state machine context to observer")
13
13
 
14
- val eventEmitter = eventEmitter
15
- if (eventEmitter == null) {
16
- logger.info(
17
- "Could not emit $eventName event; no event emitter was found.",
18
- UpdatesErrorCode.JSRuntimeError
19
- )
14
+ val observer = observer?.get() ?: run {
15
+ logger.debug("Unable to send state machine context to observer, no observer", UpdatesErrorCode.JSRuntimeError)
20
16
  return
21
17
  }
22
18
 
23
19
  try {
24
- eventEmitter.emit(eventName, context.writableMap)
25
- logger.info("Emitted event: name = $eventName")
20
+ observer.onStateMachineContextEvent(context)
21
+ logger.debug("Sent state machine context to observer")
26
22
  } catch (e: Exception) {
27
23
  logger.error(
28
- "Could not emit $eventName event",
24
+ "Could not send state machine context to observer",
29
25
  e,
30
26
  UpdatesErrorCode.JSRuntimeError
31
27
  )
@@ -141,7 +141,7 @@ class FileDownloader(
141
141
 
142
142
  val isMultipart = responseBody.contentType()?.type == "multipart"
143
143
  if (isMultipart) {
144
- parseMultipartRemoteUpdateResponse(responseBody, responseHeaderData, callback)
144
+ parseMultipartRemoteUpdateResponse(response, responseBody, responseHeaderData, callback)
145
145
  } else {
146
146
  val manifestResponseInfo = ResponsePartInfo(
147
147
  responseHeaderData = responseHeaderData,
@@ -174,38 +174,39 @@ class FileDownloader(
174
174
  }
175
175
  }
176
176
 
177
- private fun parseMultipartRemoteUpdateResponse(responseBody: ResponseBody, responseHeaderData: ResponseHeaderData, callback: RemoteUpdateDownloadCallback) {
177
+ private fun parseMultipartRemoteUpdateResponse(response: Response, responseBody: ResponseBody, responseHeaderData: ResponseHeaderData, callback: RemoteUpdateDownloadCallback) {
178
178
  var manifestPartBodyAndHeaders: Pair<String, Headers>? = null
179
179
  var extensionsBody: String? = null
180
180
  var certificateChainString: String? = null
181
181
  var directivePartBodyAndHeaders: Pair<String, Headers>? = null
182
182
 
183
- try {
184
- MultipartReader(responseBody).use { reader ->
185
- while (true) {
186
- val nextPart = reader.nextPart() ?: break
187
- nextPart.use { part ->
188
- val headers = part.headers
189
- val body = part.body
190
- val contentDispositionValue = headers["content-disposition"]
191
- if (contentDispositionValue != null) {
192
- val contentDispositionName =
193
- contentDispositionValue.parseContentDispositionNameParameter()
194
- if (contentDispositionName != null) {
195
- when (contentDispositionName) {
196
- "manifest" -> manifestPartBodyAndHeaders = Pair(body.readUtf8(), headers)
197
- "extensions" -> extensionsBody = body.readUtf8()
198
- "certificate_chain" -> certificateChainString = body.readUtf8()
199
- "directive" -> directivePartBodyAndHeaders = Pair(body.readUtf8(), headers)
183
+ val isEmpty = response.peekBody(1).bytes().isEmpty()
184
+ if (!isEmpty) {
185
+ try {
186
+ MultipartReader(responseBody).use { reader ->
187
+ while (true) {
188
+ val nextPart = reader.nextPart() ?: break
189
+ nextPart.use { part ->
190
+ val headers = part.headers
191
+ val body = part.body
192
+ val contentDispositionValue = headers["content-disposition"]
193
+ if (contentDispositionValue != null) {
194
+ val contentDispositionName =
195
+ contentDispositionValue.parseContentDispositionNameParameter()
196
+ if (contentDispositionName != null) {
197
+ when (contentDispositionName) {
198
+ "manifest" -> manifestPartBodyAndHeaders = Pair(body.readUtf8(), headers)
199
+ "extensions" -> extensionsBody = body.readUtf8()
200
+ "certificate_chain" -> certificateChainString = body.readUtf8()
201
+ "directive" -> directivePartBodyAndHeaders = Pair(body.readUtf8(), headers)
202
+ }
200
203
  }
201
204
  }
202
205
  }
203
206
  }
204
207
  }
205
- }
206
- } catch (e: Exception) {
207
- // okhttp multipart reader doesn't support empty multipart bodies, but our spec does
208
- if (responseBody.bytes().isNotEmpty()) {
208
+ } catch (e: Exception) {
209
+ // okhttp multipart reader doesn't support empty multipart bodies, but our spec does
209
210
  val message = "Error while reading multipart remote update response"
210
211
  logger.error(message, e, UpdatesErrorCode.UpdateFailedToLoad)
211
212
  callback.onFailure(IOException(message, e))
@@ -575,7 +576,7 @@ class FileDownloader(
575
576
 
576
577
  val update = UpdateFactory.getUpdate(preManifest, responseHeaderData, extensions, configuration)
577
578
  if (!SelectionPolicies.matchesFilters(update.updateEntity!!, responseHeaderData.manifestFilters)) {
578
- callback.onFailure(Exception("Manifest filters that do not manifest content for downloaded manifest"))
579
+ callback.onFailure(Exception("Manifest filters do not match manifest content for downloaded manifest"))
579
580
  } else {
580
581
  callback.onSuccess(UpdateResponsePart.ManifestUpdateResponsePart(update))
581
582
  }
@@ -294,56 +294,66 @@ class LoaderTask(
294
294
  }
295
295
 
296
296
  private fun launchFallbackUpdateFromDisk(diskUpdateCallback: LaunchUpdateCallback) {
297
- val database = databaseHolder.database
298
- val launcher = DatabaseLauncher(context, configuration, directory, fileDownloader, selectionPolicy, logger)
299
- candidateLauncher = launcher
300
- val launcherCallback: LauncherCallback = object : LauncherCallback {
301
- override fun onFailure(e: Exception) {
302
- databaseHolder.releaseDatabase()
303
- diskUpdateCallback.onFailure(e)
304
- }
297
+ AsyncTask.execute {
298
+ val database = databaseHolder.database
299
+ val launcher =
300
+ DatabaseLauncher(context, configuration, directory, fileDownloader, selectionPolicy, logger)
301
+ candidateLauncher = launcher
302
+ val launcherCallback: LauncherCallback = object : LauncherCallback {
303
+ override fun onFailure(e: Exception) {
304
+ databaseHolder.releaseDatabase()
305
+ diskUpdateCallback.onFailure(e)
306
+ }
305
307
 
306
- override fun onSuccess() {
307
- databaseHolder.releaseDatabase()
308
- diskUpdateCallback.onSuccess()
308
+ override fun onSuccess() {
309
+ databaseHolder.releaseDatabase()
310
+ diskUpdateCallback.onSuccess()
311
+ }
309
312
  }
310
- }
311
- if (configuration.hasEmbeddedUpdate) {
312
- // if the embedded update should be launched (e.g. if it's newer than any other update we have
313
- // in the database, which can happen if the app binary is updated), load it into the database
314
- // so we can launch it
315
- val embeddedUpdate = EmbeddedManifestUtils.getEmbeddedUpdate(context, configuration)!!.updateEntity
316
- val launchableUpdate = launcher.getLaunchableUpdate(database)
317
- val manifestFilters = ManifestMetadata.getManifestFilters(database, configuration)
318
- if (selectionPolicy.shouldLoadNewUpdate(embeddedUpdate, launchableUpdate, manifestFilters)) {
319
- EmbeddedLoader(context, configuration, logger, database, directory).start(object : LoaderCallback {
320
- override fun onFailure(e: Exception) {
321
- logger.error("Unexpected error copying embedded update", e, UpdatesErrorCode.Unknown)
322
- launcher.launch(database, launcherCallback)
323
- }
313
+ if (configuration.hasEmbeddedUpdate) {
314
+ // if the embedded update should be launched (e.g. if it's newer than any other update we have
315
+ // in the database, which can happen if the app binary is updated), load it into the database
316
+ // so we can launch it
317
+ val embeddedUpdate =
318
+ EmbeddedManifestUtils.getEmbeddedUpdate(context, configuration)!!.updateEntity
319
+ val launchableUpdate = launcher.getLaunchableUpdate(database)
320
+ val manifestFilters = ManifestMetadata.getManifestFilters(database, configuration)
321
+ if (selectionPolicy.shouldLoadNewUpdate(
322
+ embeddedUpdate,
323
+ launchableUpdate,
324
+ manifestFilters
325
+ )
326
+ ) {
327
+ EmbeddedLoader(context, configuration, logger, database, directory).start(object :
328
+ LoaderCallback {
329
+ override fun onFailure(e: Exception) {
330
+ logger.error("Unexpected error copying embedded update", e, UpdatesErrorCode.Unknown)
331
+ launcher.launch(database, launcherCallback)
332
+ }
324
333
 
325
- override fun onSuccess(loaderResult: Loader.LoaderResult) {
326
- launcher.launch(database, launcherCallback)
327
- }
334
+ override fun onSuccess(loaderResult: Loader.LoaderResult) {
335
+ launcher.launch(database, launcherCallback)
336
+ }
328
337
 
329
- override fun onAssetLoaded(
330
- asset: AssetEntity,
331
- successfulAssetCount: Int,
332
- failedAssetCount: Int,
333
- totalAssetCount: Int
334
- ) {
335
- // do nothing
336
- }
338
+ override fun onAssetLoaded(
339
+ asset: AssetEntity,
340
+ successfulAssetCount: Int,
341
+ failedAssetCount: Int,
342
+ totalAssetCount: Int
343
+ ) {
344
+ // do nothing
345
+ }
337
346
 
338
- override fun onUpdateResponseLoaded(updateResponse: UpdateResponse): Loader.OnUpdateResponseLoadedResult {
339
- return Loader.OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = true)
340
- }
341
- })
347
+ override fun onUpdateResponseLoaded(updateResponse: UpdateResponse): Loader.OnUpdateResponseLoadedResult {
348
+ return Loader.OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = true)
349
+ }
350
+ })
351
+ } else {
352
+ launcher.launch(database, launcherCallback)
353
+ }
342
354
  } else {
343
355
  launcher.launch(database, launcherCallback)
344
356
  }
345
- } else {
346
- launcher.launch(database, launcherCallback)
347
357
  }
348
358
  }
349
359
 
@@ -439,33 +449,35 @@ class LoaderTask(
439
449
  object : LauncherCallback {
440
450
  override fun onFailure(e: Exception) {
441
451
  databaseHolder.releaseDatabase()
452
+ callback.onRemoteUpdateFinished(
453
+ RemoteUpdateStatus.ERROR,
454
+ null,
455
+ e
456
+ )
442
457
  remoteUpdateCallback.onFailure(e)
443
458
  logger.error("Loaded new update but it failed to launch", e, UpdatesErrorCode.UpdateFailedToLoad)
444
459
  }
445
460
 
446
461
  override fun onSuccess() {
447
462
  databaseHolder.releaseDatabase()
448
- val hasLaunchedSynchronized = synchronized(this@LoaderTask) {
463
+ synchronized(this@LoaderTask) {
449
464
  if (!hasLaunched) {
450
465
  candidateLauncher = newLauncher
451
466
  isUpToDate = true
452
467
  }
453
- hasLaunched
454
468
  }
455
- if (hasLaunchedSynchronized) {
456
- if (availableUpdate == null) {
457
- callback.onRemoteUpdateFinished(
458
- RemoteUpdateStatus.NO_UPDATE_AVAILABLE,
459
- null,
460
- null
461
- )
462
- } else {
463
- callback.onRemoteUpdateFinished(
464
- RemoteUpdateStatus.UPDATE_AVAILABLE,
465
- availableUpdate,
466
- null
467
- )
468
- }
469
+ if (availableUpdate == null) {
470
+ callback.onRemoteUpdateFinished(
471
+ RemoteUpdateStatus.NO_UPDATE_AVAILABLE,
472
+ null,
473
+ null
474
+ )
475
+ } else {
476
+ callback.onRemoteUpdateFinished(
477
+ RemoteUpdateStatus.UPDATE_AVAILABLE,
478
+ availableUpdate,
479
+ null
480
+ )
469
481
  }
470
482
  remoteUpdateCallback.onSuccess()
471
483
  }
@@ -27,7 +27,7 @@ class RecreateReactContextProcedure(
27
27
  Handler(Looper.getMainLooper()).post {
28
28
  reactApplication.restart(weakActivity?.get(), "Restart from RecreateReactContextProcedure")
29
29
  }
30
- procedureContext.resetState()
30
+ procedureContext.resetStateAfterRestart()
31
31
  procedureContext.onComplete()
32
32
  }
33
33
  }
@@ -7,8 +7,8 @@ import android.os.Handler
7
7
  import android.os.Looper
8
8
  import com.facebook.react.ReactApplication
9
9
  import com.facebook.react.bridge.JSBundleLoader
10
- import com.facebook.react.config.ReactFeatureFlags
11
10
  import expo.modules.core.interfaces.ReactNativeHostHandler
11
+ import expo.modules.rncompatibility.ReactNativeFeatureFlags
12
12
  import expo.modules.updates.UpdatesConfiguration
13
13
  import expo.modules.updates.db.DatabaseHolder
14
14
  import expo.modules.updates.db.Reaper
@@ -83,7 +83,7 @@ class RelaunchProcedure(
83
83
  if (shouldRunReaper) {
84
84
  runReaper()
85
85
  }
86
- procedureContext.resetState()
86
+ procedureContext.resetStateAfterRestart()
87
87
  procedureContext.onComplete()
88
88
  }
89
89
  }
@@ -118,7 +118,7 @@ class RelaunchProcedure(
118
118
  reactApplication: ReactApplication,
119
119
  launchAssetFile: String
120
120
  ) {
121
- if (ReactFeatureFlags.enableBridgelessArchitecture) {
121
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
122
122
  return
123
123
  }
124
124
 
@@ -3,7 +3,7 @@ package expo.modules.updates.procedures
3
3
  import android.app.Activity
4
4
  import com.facebook.react.ReactApplication
5
5
  import com.facebook.react.common.LifecycleState
6
- import com.facebook.react.config.ReactFeatureFlags
6
+ import expo.modules.rncompatibility.ReactNativeFeatureFlags
7
7
 
8
8
  /**
9
9
  * An extension for [ReactApplication] to restart the app
@@ -12,7 +12,7 @@ import com.facebook.react.config.ReactFeatureFlags
12
12
  * @param reason The restart reason. Only used on bridgeless mode.
13
13
  */
14
14
  internal fun ReactApplication.restart(activity: Activity?, reason: String) {
15
- if (ReactFeatureFlags.enableBridgelessArchitecture) {
15
+ if (ReactNativeFeatureFlags.enableBridgelessArchitecture) {
16
16
  val reactHost = this.reactHost
17
17
  check(reactHost != null)
18
18
  if (reactHost.lifecycleState != LifecycleState.RESUMED && activity != null) {
@@ -102,6 +102,7 @@ class StartupProcedure(
102
102
  }
103
103
 
104
104
  override fun onFinishedAllLoading() {
105
+ procedureContext.processStateEvent(UpdatesStateEvent.EndStartup())
105
106
  procedureContext.onComplete()
106
107
  }
107
108
 
@@ -208,6 +209,7 @@ class StartupProcedure(
208
209
 
209
210
  override fun run(procedureContext: ProcedureContext) {
210
211
  this.procedureContext = procedureContext
212
+ procedureContext.processStateEvent(UpdatesStateEvent.StartStartup())
211
213
  initializeDatabaseHandler()
212
214
  initializeErrorRecovery()
213
215
  loaderTask.start()