@mentra/crust 3.3.0-dev.577 → 3.3.0-dev.581

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.
@@ -8,6 +8,12 @@ import expo.modules.kotlin.modules.Module
8
8
  import expo.modules.kotlin.modules.ModuleDefinition
9
9
  import expo.modules.kotlin.functions.Queues
10
10
  import java.net.URL
11
+ import java.util.concurrent.Executors
12
+ import kotlinx.coroutines.CoroutineName
13
+ import kotlinx.coroutines.CoroutineScope
14
+ import kotlinx.coroutines.SupervisorJob
15
+ import kotlinx.coroutines.asCoroutineDispatcher
16
+ import kotlinx.coroutines.cancel
11
17
 
12
18
  import com.mentra.crust.navigation.NavigationManager
13
19
  import com.mentra.crust.heading.HeadingManager
@@ -73,6 +79,22 @@ class CrustModule : Module() {
73
79
  // __dispatch from a per-miniapp QuickJS context (SUBSCRIBE, mic, location,
74
80
  // display, send, etc.) would be silently dropped on Android.
75
81
  @Volatile private var runtimeInstalled: Boolean = false
82
+
83
+ // MentraJS calls get their own serial queue. Expo runs every module's default-queue
84
+ // AsyncFunction on one shared thread, so a blocking call in another module held every
85
+ // host→miniapp message behind it: the ACS scoped Wi-Fi join waits on the glasses hotspot for
86
+ // tens of seconds, the miniapp never saw the host's PING, and the host respawned Mentra Call
87
+ // mid-join. Serial, so spawn, dispatch and kill still run in the order JS issued them.
88
+ private val mentraJsExecutor =
89
+ Executors.newSingleThreadExecutor { r ->
90
+ Thread(r, "MentraJS-bridge").apply { isDaemon = true }
91
+ }
92
+ private val mentraJsQueue =
93
+ CoroutineScope(
94
+ mentraJsExecutor.asCoroutineDispatcher() +
95
+ SupervisorJob() +
96
+ CoroutineName("MentraJS-bridge"),
97
+ )
76
98
  private var notificationEventReceiver: BroadcastReceiver? = null
77
99
  private var notificationBridgeContext: android.content.Context? = null
78
100
 
@@ -145,6 +167,8 @@ class CrustModule : Module() {
145
167
  notificationEventReceiver = null
146
168
  notificationBridgeContext = null
147
169
  eventEmitter = null
170
+ mentraJsQueue.cancel()
171
+ mentraJsExecutor.shutdown()
148
172
  }
149
173
 
150
174
  Function("hello") {
@@ -260,7 +284,7 @@ class CrustModule : Module() {
260
284
  polyfillBundleOverride = polyfillBundle.takeIf { it.isNotEmpty() },
261
285
  miniappJs = miniappJs,
262
286
  )
263
- }
287
+ }.runOnQueue(mentraJsQueue)
264
288
 
265
289
  AsyncFunction("mentraJsEvaluate") { packageName: String, source: String ->
266
290
  val ctx =
@@ -268,7 +292,7 @@ class CrustModule : Module() {
268
292
  ?: appContext.currentActivity
269
293
  ?: throw IllegalStateException("MentraJS: no context")
270
294
  JSCRuntime.shared(ctx).evaluate(packageName, source)
271
- }
295
+ }.runOnQueue(mentraJsQueue)
272
296
 
273
297
  AsyncFunction("mentraJsKill") { packageName: String ->
274
298
  val ctx =
@@ -276,7 +300,7 @@ class CrustModule : Module() {
276
300
  ?: appContext.currentActivity
277
301
  ?: throw IllegalStateException("MentraJS: no context")
278
302
  JSCRuntime.shared(ctx).kill(packageName)
279
- }
303
+ }.runOnQueue(mentraJsQueue)
280
304
 
281
305
  AsyncFunction("mentraJsDispatchToJs") { packageName: String, envelope: Map<String, Any?> ->
282
306
  val ctx =
@@ -285,7 +309,7 @@ class CrustModule : Module() {
285
309
  ?: throw IllegalStateException("MentraJS: no context")
286
310
  val json = org.json.JSONObject(envelope as Map<*, *>).toString()
287
311
  JSCRuntime.shared(ctx).dispatchToJs(packageName, json)
288
- }
312
+ }.runOnQueue(mentraJsQueue)
289
313
 
290
314
  AsyncFunction("mentraJsSetManifest") { packageName: String, permissions: List<String> ->
291
315
  val ctx =
@@ -296,7 +320,7 @@ class CrustModule : Module() {
296
320
  packageName,
297
321
  InstalledMiniappManifest(permissions.toSet()),
298
322
  )
299
- }
323
+ }.runOnQueue(mentraJsQueue)
300
324
 
301
325
  Function("mentraJsAlivePackages") {
302
326
  val ctx =
@@ -314,7 +338,7 @@ class CrustModule : Module() {
314
338
  ?: appContext.currentActivity
315
339
  ?: return@AsyncFunction false
316
340
  JSCRuntime.shared(ctx).debugForceGC(packageName)
317
- }
341
+ }.runOnQueue(mentraJsQueue)
318
342
 
319
343
  Function("mentraJsLoadPolyfillBundle") {
320
344
  val ctx =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/crust",
3
- "version": "3.3.0-dev.577",
3
+ "version": "3.3.0-dev.581",
4
4
  "description": "Mentra Native Module",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -35,7 +35,7 @@
35
35
  "license": "Apache-2.0",
36
36
  "homepage": "https://github.com/Mentra-Community/MentraOS/tree/dev/mobile/modules/crust#readme",
37
37
  "dependencies": {
38
- "@mentra/jspolyfill": "3.3.0-dev.577"
38
+ "@mentra/jspolyfill": "3.3.0-dev.581"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@expo/config-plugins": ">=8.0.0",