@glassly/crust 0.1.0-dev.99 → 0.2.8-dev.444

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.
@@ -9,7 +9,7 @@ applyKotlinExpoModulesCorePlugin()
9
9
  useCoreDependencies()
10
10
  useExpoPublishing()
11
11
 
12
- // GlasslyJS Android runtime: QuickJS via dokar3/quickjs-kt.
12
+ // JSRuntime Android runtime: QuickJS via dokar3/quickjs-kt.
13
13
  //
14
14
  // Zipline's `bind(name, service)` registers services in an internal Kotlin
15
15
  // map keyed by name for the Kotlin/JS-compiled `Zipline.take()` API; it does
@@ -51,7 +51,7 @@ android {
51
51
  versionName "0.1.0"
52
52
  multiDexEnabled true
53
53
  }
54
- // Pull the GlasslyJS polyfill bundle from the sibling @glassly/jspolyfill
54
+ // Pull the JSRuntime polyfill bundle from the sibling @glassly/jspolyfill
55
55
  // module's `assets/` directory — single committed source of truth. The
56
56
  // file is read at runtime via `appContext.assets.open("startup.js")`.
57
57
  // Cross-module sourceSet entry beats checking the same artifact into git
@@ -117,7 +117,7 @@ dependencies {
117
117
  }
118
118
 
119
119
  dependencies {
120
- // GlasslyJS — QuickJS for per-miniapp JS contexts. dokar3/quickjs-kt is a
120
+ // JSRuntime — QuickJS for per-miniapp JS contexts. dokar3/quickjs-kt is a
121
121
  // KMP wrapper around Bellard QuickJS that exposes `function(name) { ... }`
122
122
  // for installing Kotlin lambdas as JS globals via `JS_SetPropertyStr`.
123
123
  // Mirrors iOS Apple JSC's `setObject(_, forKeyedSubscript:)` pattern.
@@ -7,7 +7,7 @@
7
7
  <application>
8
8
  <service
9
9
  android:name="com.glassly.crust.services.NotificationListenerServiceImpl"
10
- android:label="@string/glassly_crust_notification_listener_label"
10
+ android:label="@string/crust_notification_listener_label"
11
11
  android:enabled="false"
12
12
  android:exported="false"
13
13
  android:process=":notif"
@@ -1,6 +1,12 @@
1
1
  package com.glassly.crust
2
2
 
3
+ import android.app.KeyguardManager
3
4
  import android.content.BroadcastReceiver
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.content.IntentFilter
8
+ import android.os.Build
9
+ import android.os.PowerManager
4
10
  import android.util.Log
5
11
  import com.glassly.crust.services.NotificationListener
6
12
  import com.glassly.crust.services.NotificationProcessBridge
@@ -13,6 +19,8 @@ import com.glassly.crust.heading.HeadingManager
13
19
  import com.glassly.crust.jsc.JSCRuntime
14
20
  import com.glassly.crust.jsc.InstalledMiniappManifest
15
21
  import com.glassly.crust.jsc.JSCPolyfillBridge
22
+ import com.glassly.crust.utils.ImageProcessor
23
+ import com.glassly.crust.utils.VideoStabilizer
16
24
 
17
25
  class CrustModule : Module() {
18
26
  companion object {
@@ -78,6 +86,47 @@ class CrustModule : Module() {
78
86
  @Volatile private var runtimeInstalled: Boolean = false
79
87
  private var notificationEventReceiver: BroadcastReceiver? = null
80
88
  private var notificationBridgeContext: android.content.Context? = null
89
+ private var lockStateReceiver: BroadcastReceiver? = null
90
+ private var lockStateContext: Context? = null
91
+
92
+ /**
93
+ * Phone lock state as seen by the app: `unlocked` = screen on and no keyguard
94
+ * showing; `passcodeSet` = the keyguard is secured (PIN/pattern/biometric).
95
+ * Mirrors the iOS protected-data signal so the engine can treat both alike.
96
+ */
97
+ private fun phoneLockState(context: Context): Map<String, Any> {
98
+ val keyguard = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
99
+ val power = context.getSystemService(Context.POWER_SERVICE) as PowerManager
100
+ val unlocked = power.isInteractive && !keyguard.isKeyguardLocked
101
+ return mapOf("unlocked" to unlocked, "passcodeSet" to keyguard.isDeviceSecure)
102
+ }
103
+
104
+ private fun registerLockStateReceiverIfPossible(): Boolean {
105
+ if (lockStateReceiver != null) return true
106
+ val context = appContext.reactContext ?: appContext.currentActivity ?: return false
107
+ val applicationContext = context.applicationContext
108
+ val receiver =
109
+ object : BroadcastReceiver() {
110
+ override fun onReceive(ctx: Context, intent: Intent) {
111
+ emitEvent("phone_lock_state", phoneLockState(ctx.applicationContext))
112
+ }
113
+ }
114
+ val filter =
115
+ IntentFilter().apply {
116
+ addAction(Intent.ACTION_SCREEN_ON)
117
+ addAction(Intent.ACTION_SCREEN_OFF)
118
+ addAction(Intent.ACTION_USER_PRESENT)
119
+ }
120
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
121
+ applicationContext.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
122
+ } else {
123
+ @Suppress("UnspecifiedRegisterReceiverFlag")
124
+ applicationContext.registerReceiver(receiver, filter)
125
+ }
126
+ lockStateReceiver = receiver
127
+ lockStateContext = applicationContext
128
+ return true
129
+ }
81
130
 
82
131
  private fun registerNotificationBridgeIfPossible(): Boolean {
83
132
  if (notificationEventReceiver != null) return true
@@ -98,16 +147,16 @@ class CrustModule : Module() {
98
147
  if (runtimeInstalled) return true
99
148
  val ctx = appContext.reactContext ?: appContext.currentActivity
100
149
  if (ctx == null) {
101
- Log.w(TAG, "GlasslyJS: install deferred ($reason) — no context yet")
150
+ Log.w(TAG, "JSRuntime: install deferred ($reason) — no context yet")
102
151
  return false
103
152
  }
104
153
  val runtime = JSCRuntime.shared(ctx)
105
154
  runtime.onOutbound = { msg ->
106
- sendEvent("glasslyjs_message", msg.payload)
155
+ sendEvent("js_message", msg.payload)
107
156
  }
108
157
  JSCPolyfillBridge.install(runtime)
109
158
  runtimeInstalled = true
110
- Log.i(TAG, "GlasslyJS: runtime installed ($reason)")
159
+ Log.i(TAG, "JSRuntime: runtime installed ($reason)")
111
160
  return true
112
161
  }
113
162
 
@@ -129,14 +178,17 @@ class CrustModule : Module() {
129
178
  "onNavRoute",
130
179
  "onNavOffRoute",
131
180
  "onHeading",
132
- // GlasslyJS — per-miniapp JSContext outbound message bus.
181
+ // Fires on screen on/off and unlock. Payload matches getPhoneLockState().
182
+ "phone_lock_state",
183
+ // JSRuntime — per-miniapp JSContext outbound message bus.
133
184
  // JSRouter subscribes via Crust.addListener.
134
- "glasslyjs_message",
185
+ "js_message",
135
186
  )
136
187
 
137
188
  OnCreate {
138
189
  eventEmitter = { eventName, data -> sendEvent(eventName, data) }
139
190
  registerNotificationBridgeIfPossible()
191
+ registerLockStateReceiverIfPossible()
140
192
  installRuntimeIfPossible("OnCreate")
141
193
  }
142
194
 
@@ -148,6 +200,13 @@ class CrustModule : Module() {
148
200
  }
149
201
  notificationEventReceiver = null
150
202
  notificationBridgeContext = null
203
+ val lockCtx = lockStateContext
204
+ val lockReceiver = lockStateReceiver
205
+ if (lockCtx != null && lockReceiver != null) {
206
+ runCatching { lockCtx.unregisterReceiver(lockReceiver) }
207
+ }
208
+ lockStateReceiver = null
209
+ lockStateContext = null
151
210
  eventEmitter = null
152
211
  }
153
212
 
@@ -220,6 +279,15 @@ class CrustModule : Module() {
220
279
  NotificationListener.hasNotificationListenerPermission(context)
221
280
  }
222
281
 
282
+ AsyncFunction("getPhoneLockState") {
283
+ registerLockStateReceiverIfPossible()
284
+ val context =
285
+ appContext.reactContext
286
+ ?: appContext.currentActivity
287
+ ?: throw IllegalStateException("No context available")
288
+ phoneLockState(context)
289
+ }
290
+
223
291
  AsyncFunction("refreshNotificationListener") {
224
292
  val context =
225
293
  appContext.reactContext
@@ -237,13 +305,13 @@ class CrustModule : Module() {
237
305
  true
238
306
  }
239
307
 
240
- // MARK: - GlasslyJS Runtime
308
+ // MARK: - JS Runtime
241
309
 
242
310
  AsyncFunction("jsSpawn") { packageName: String, polyfillBundle: String, miniappJs: String ->
243
311
  val ctx =
244
312
  appContext.reactContext
245
313
  ?: appContext.currentActivity
246
- ?: throw IllegalStateException("GlasslyJS: no context")
314
+ ?: throw IllegalStateException("JSRuntime: no context")
247
315
  // Safety net for the OnCreate-too-early race: if the runtime wasn't
248
316
  // wired during module creation (no reactContext yet), do it now —
249
317
  // otherwise every host-bound __dispatch from this miniapp's JSContext
@@ -260,7 +328,7 @@ class CrustModule : Module() {
260
328
  val ctx =
261
329
  appContext.reactContext
262
330
  ?: appContext.currentActivity
263
- ?: throw IllegalStateException("GlasslyJS: no context")
331
+ ?: throw IllegalStateException("JSRuntime: no context")
264
332
  JSCRuntime.shared(ctx).evaluate(packageName, source)
265
333
  }
266
334
 
@@ -268,7 +336,7 @@ class CrustModule : Module() {
268
336
  val ctx =
269
337
  appContext.reactContext
270
338
  ?: appContext.currentActivity
271
- ?: throw IllegalStateException("GlasslyJS: no context")
339
+ ?: throw IllegalStateException("JSRuntime: no context")
272
340
  JSCRuntime.shared(ctx).kill(packageName)
273
341
  }
274
342
 
@@ -276,7 +344,7 @@ class CrustModule : Module() {
276
344
  val ctx =
277
345
  appContext.reactContext
278
346
  ?: appContext.currentActivity
279
- ?: throw IllegalStateException("GlasslyJS: no context")
347
+ ?: throw IllegalStateException("JSRuntime: no context")
280
348
  val json = org.json.JSONObject(envelope as Map<*, *>).toString()
281
349
  JSCRuntime.shared(ctx).dispatchToJs(packageName, json)
282
350
  }
@@ -285,7 +353,7 @@ class CrustModule : Module() {
285
353
  val ctx =
286
354
  appContext.reactContext
287
355
  ?: appContext.currentActivity
288
- ?: throw IllegalStateException("GlasslyJS: no context")
356
+ ?: throw IllegalStateException("JSRuntime: no context")
289
357
  JSCRuntime.shared(ctx).dispatcher.setManifest(
290
358
  packageName,
291
359
  InstalledMiniappManifest(permissions.toSet()),
@@ -482,7 +550,7 @@ class CrustModule : Module() {
482
550
  val colorCorrection = options["colorCorrection"] as? Boolean ?: true
483
551
 
484
552
  val processingTimeMs =
485
- com.glassly.crust.utils.ImageProcessor.process(
553
+ ImageProcessor.process(
486
554
  inputPath,
487
555
  outputPath,
488
556
  lensCorrection,
@@ -514,7 +582,7 @@ class CrustModule : Module() {
514
582
  outputPath: String ->
515
583
  try {
516
584
  val processingTimeMs =
517
- com.glassly.crust.utils.ImageProcessor.mergeHdr(
585
+ ImageProcessor.mergeHdr(
518
586
  underPath,
519
587
  normalPath,
520
588
  overPath,
@@ -550,7 +618,7 @@ class CrustModule : Module() {
550
618
  }
551
619
 
552
620
  val processingTimeMs =
553
- com.glassly.crust.utils.VideoStabilizer.stabilize(inputPath, imuPath, outputPath)
621
+ VideoStabilizer.stabilize(inputPath, imuPath, outputPath)
554
622
 
555
623
  if (processingTimeMs >= 0) {
556
624
  mapOf(
@@ -7,7 +7,7 @@ import java.security.SecureRandom
7
7
  import org.json.JSONArray
8
8
 
9
9
  /**
10
- * GlasslyJS dispatch table + permission gate for the per-miniapp
10
+ * JSRuntime dispatch table + permission gate for the per-miniapp
11
11
  * `__dispatch(iface, method, args)` bridge. Symmetric with iOS
12
12
  * [JSCDispatcher.swift].
13
13
  *
@@ -26,7 +26,7 @@ sealed class JSCDispatchOutcome {
26
26
  object Async : JSCDispatchOutcome()
27
27
  /** Refused. JS sees a thrown Error with `code` + `message`. */
28
28
  data class Error(val code: String, val message: String?) : JSCDispatchOutcome()
29
- /** Forward to RN via the `glasslyjs_message` Expo event. */
29
+ /** Forward to RN via the `js_message` Expo event. */
30
30
  data class ForwardToRn(val payload: Map<String, Any?>) : JSCDispatchOutcome()
31
31
  }
32
32
 
@@ -34,7 +34,7 @@ data class InstalledMiniappManifest(val permissions: Set<String>)
34
34
 
35
35
  class JSCDispatcher(private val appContext: Context) {
36
36
  companion object {
37
- private const val TAG = "GlasslyJS.Dispatcher"
37
+ private const val TAG = "JSRuntime.Dispatcher"
38
38
  }
39
39
 
40
40
  private val routes = mutableMapOf<String, Handler>()
@@ -184,6 +184,6 @@ class JSCDispatcher(private val appContext: Context) {
184
184
  }
185
185
 
186
186
  private fun prefs(packageName: String): SharedPreferences {
187
- return appContext.getSharedPreferences("GlasslyJS-$packageName", Context.MODE_PRIVATE)
187
+ return appContext.getSharedPreferences("JSRuntime-$packageName", Context.MODE_PRIVATE)
188
188
  }
189
189
  }
@@ -35,7 +35,7 @@ import android.util.Base64
35
35
  * `JS_ExecutePendingJob` — see the spec's Android microtask section.
36
36
  */
37
37
  object JSCPolyfillBridge {
38
- private const val TAG = "GlasslyJS.PolyfillBridge"
38
+ private const val TAG = "JSRuntime.PolyfillBridge"
39
39
 
40
40
  private val httpClient: OkHttpClient by lazy {
41
41
  OkHttpClient.Builder()
@@ -15,7 +15,7 @@ import kotlinx.coroutines.Dispatchers
15
15
  import kotlinx.coroutines.runBlocking
16
16
 
17
17
  /**
18
- * Thrown from the `__dispatch` binding to propagate a structured GlasslyJS
18
+ * Thrown from the `__dispatch` binding to propagate a structured JSRuntime
19
19
  * dispatch failure (PERMISSION_NOT_DECLARED / INVALID_ARGS / etc.) back to
20
20
  * the calling JS frame. Dokar3 surfaces Kotlin throwables from bindings as
21
21
  * JS-side `throw new Error(message)`, so the SDK's send-request Promise
@@ -24,7 +24,7 @@ import kotlinx.coroutines.runBlocking
24
24
  class JSDispatchError(val code: String, message: String) : RuntimeException("$code: $message")
25
25
 
26
26
  /**
27
- * GlasslyJS — per-miniapp QuickJS runtime host on Android.
27
+ * JSRuntime — per-miniapp QuickJS runtime host on Android.
28
28
  *
29
29
  * Owns N QuickJs instances (via dokar3/quickjs-kt) keyed by packageName.
30
30
  * Each context gets:
@@ -66,7 +66,7 @@ class JSDispatchError(val code: String, message: String) : RuntimeException("$co
66
66
  */
67
67
  class JSCRuntime private constructor(private val appContext: Context) {
68
68
  companion object {
69
- private const val TAG = "GlasslyJS"
69
+ private const val TAG = "JSRuntime"
70
70
 
71
71
  // NACK cold-start timeout (15s — covers polyfill + init on slow devices).
72
72
  const val COLD_START_NACK_TIMEOUT_MS: Long = 15_000
@@ -87,7 +87,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
87
87
  }
88
88
 
89
89
  /**
90
- * Subscribe to outbound `glasslyjs_message` events. Set by [CrustModule]
90
+ * Subscribe to outbound `js_message` events. Set by [CrustModule]
91
91
  * during `OnCreate` (or lazily on `jsSpawn` if reactContext was
92
92
  * null at OnCreate time). The runtime fires this for every __dispatch
93
93
  * call that has no matching local route in [JSCDispatcher]. If this is
@@ -139,7 +139,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
139
139
  // QuickJs single-thread executor, so timer fires happen on the JS thread.
140
140
  private val timerScheduler: ScheduledExecutorService =
141
141
  Executors.newScheduledThreadPool(1) { r ->
142
- Thread(r, "GlasslyJS-timer-scheduler").apply { isDaemon = true }
142
+ Thread(r, "JSRuntime-timer-scheduler").apply { isDaemon = true }
143
143
  }
144
144
 
145
145
  private inner class ContextRecord(
@@ -211,7 +211,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
211
211
  fun alivePackages(): List<String> = contexts.keys.toList()
212
212
 
213
213
  /**
214
- * Read the bundled GlasslyJS polyfill (`assets/startup.js`) shipped
214
+ * Read the bundled JSRuntime polyfill (`assets/startup.js`) shipped
215
215
  * inside the host APK. The file is sourced from the sibling
216
216
  * @glassly/jspolyfill module via `sourceSets.main.assets.srcDirs`
217
217
  * in this module's build.gradle. Cached after first read.
@@ -240,7 +240,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
240
240
  kill(packageName)
241
241
  }
242
242
  val executor = Executors.newSingleThreadExecutor { r ->
243
- Thread(r, "GlasslyJS-$packageName").apply { isDaemon = true }
243
+ Thread(r, "JSRuntime-$packageName").apply { isDaemon = true }
244
244
  }
245
245
 
246
246
  // Create the QuickJs context ON the executor thread. We pass
@@ -288,10 +288,10 @@ class JSCRuntime private constructor(private val appContext: Context) {
288
288
  runBlocking {
289
289
  installGlobals(qjs, packageName)
290
290
  if (bundle.isNotEmpty()) {
291
- qjs.evaluate<Any?>(bundle, filename = "glasslyjs:startup.js")
291
+ qjs.evaluate<Any?>(bundle, filename = "jsruntime:startup.js")
292
292
  }
293
293
  if (miniappJs.isNotEmpty()) {
294
- qjs.evaluate<Any?>(miniappJs, filename = "glasslyjs:miniapp.js")
294
+ qjs.evaluate<Any?>(miniappJs, filename = "jsruntime:miniapp.js")
295
295
  }
296
296
  }
297
297
  Log.i(TAG, "spawned $packageName")
@@ -417,7 +417,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
417
417
  try {
418
418
  record.qjs.evaluate<Any?>(
419
419
  "globalThis.__deliverTimer && globalThis.__deliverTimer($token);",
420
- filename = "glasslyjs:timer-$token.js",
420
+ filename = "jsruntime:timer-$token.js",
421
421
  )
422
422
  } catch (e: Throwable) {
423
423
  Log.w(TAG, "timer fire threw in $packageName: ${e.message}")
@@ -443,7 +443,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
443
443
  runBlocking {
444
444
  record.qjs.evaluate<Any?>(
445
445
  source,
446
- filename = "glasslyjs:eval-${System.nanoTime()}.js",
446
+ filename = "jsruntime:eval-${System.nanoTime()}.js",
447
447
  )
448
448
  }
449
449
  }.get()
@@ -493,7 +493,7 @@ class JSCRuntime private constructor(private val appContext: Context) {
493
493
  try {
494
494
  val source = "globalThis.__deliver(${jsStringLiteral(envelopeJson)});"
495
495
  runBlocking {
496
- record.qjs.evaluate<Any?>(source, filename = "glasslyjs:deliver.js")
496
+ record.qjs.evaluate<Any?>(source, filename = "jsruntime:deliver.js")
497
497
  }
498
498
  // Successful delivery — context is responsive.
499
499
  record.readyNackTimer?.cancel(false)
@@ -19,7 +19,7 @@ import java.util.concurrent.ConcurrentHashMap
19
19
  class NotificationListener private constructor(private val context: Context) {
20
20
  companion object {
21
21
  private const val TAG = "CrustNotificationListener"
22
- private const val PREFS_NAME = "glassly_crust_notification_prefs"
22
+ private const val PREFS_NAME = "crust_notification_prefs"
23
23
  private const val PREF_LISTENER_ENABLED = "notification_listener_enabled"
24
24
  private const val PREF_NOTIFICATIONS_BLOCKLIST = "notifications_blocklist"
25
25
 
@@ -1,3 +1,3 @@
1
1
  <resources>
2
- <string name="glassly_crust_notification_listener_label">Glassly</string>
2
+ <string name="crust_notification_listener_label">Glassly</string>
3
3
  </resources>
@@ -12,6 +12,17 @@ export type CrustModuleEvents = {
12
12
  onNavRoute: (params: NavRoutePayload) => void;
13
13
  onNavOffRoute: (params: NavOffRoutePayload) => void;
14
14
  onHeading: (params: HeadingPayload) => void;
15
+ phone_lock_state: (params: PhoneLockState) => void;
16
+ };
17
+ /**
18
+ * Whether the user can see the phone right now. iOS derives `unlocked` from
19
+ * protected-data availability, Android from screen on + keyguard hidden.
20
+ * Without a passcode iOS never protects data, so `unlocked` is always true
21
+ * there and carries no information; check `passcodeSet` first.
22
+ */
23
+ export type PhoneLockState = {
24
+ unlocked: boolean;
25
+ passcodeSet: boolean;
15
26
  };
16
27
  export type NavOffRoutePayload = {
17
28
  /** Approximate perpendicular distance in meters from the route. */
@@ -1 +1 @@
1
- {"version":3,"file":"Crust.types.d.ts","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAC,MAAM,cAAc,CAAA;AAEtD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC9C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAAA;IACvD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAAA;IACrD,UAAU,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,UAAU,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,aAAa,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACnD,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,mEAAmE;IACnE,sBAAsB,EAAE,MAAM,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC,CAAA;IACzC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,kEAAkE;IAClE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC3D,4BAA4B,EAAE,CAAC,KAAK,EAAE,+BAA+B,KAAK,IAAI,CAAA;IAC9E,wBAAwB,EAAE,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,CAAA;CACvE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,2FAA2F;IAC3F,cAAc,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,4EAA4E;IAC5E,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,2DAA2D;IAC3D,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,KAAK,EAAE;QAAC,WAAW,EAAE,kBAAkB,CAAA;KAAC,KAAK,IAAI,CAAA;IAC1D,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;CAC7B,CAAA"}
1
+ {"version":3,"file":"Crust.types.d.ts","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Crust.types.js","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {StyleProp, ViewStyle} from \"react-native\"\n\nexport type OnLoadEventPayload = {\n url: string\n}\n\nexport type CrustModuleEvents = {\n onChange: (params: ChangeEventPayload) => void\n onNavManeuver: (params: NavManeuverPayload) => void\n onNavRerouting: (params: Record<string, never>) => void\n onNavArrived: (params: Record<string, never>) => void\n onNavError: (params: NavErrorPayload) => void\n onNavLocation: (params: NavLocationPayload) => void\n onNavRoute: (params: NavRoutePayload) => void\n onNavOffRoute: (params: NavOffRoutePayload) => void\n onHeading: (params: HeadingPayload) => void\n}\n\nexport type NavOffRoutePayload = {\n /** Approximate perpendicular distance in meters from the route. */\n offRouteDistanceMeters: number\n}\n\nexport type HeadingPayload = {\n /** Compass heading in degrees, 0 = north, 90 = east. */\n degrees: number\n}\n\nexport type NavRoutePayload = {\n points: Array<{lat: number; lng: number}>\n /**\n * Ordered step list along the route, when supplied by the host. Each\n * step is one navigable segment — typically one straight stretch of\n * road ending in a maneuver. Optional for back-compat with older\n * hosts that only ship the polyline. The SDK's pivot module consumes\n * this to enrich pivots with `fromRoad` / `toRoad` metadata.\n */\n steps?: NavRouteStepPayload[]\n}\n\nexport type NavRouteStepPayload = {\n /** Coordinate where this step begins (== end of the previous step). */\n lat: number\n lng: number\n /**\n * Index into `NavRoutePayload.points[]` where this step starts.\n * Lets consumers correlate step boundaries with polyline geometry.\n */\n routeIndex: number\n /** Name of the road traversed during this step. Null when the engine has no name. */\n road?: string | null\n /**\n * Categorical maneuver performed at the END of this step (i.e. how\n * the user exits this step). Same vocabulary as `NavManeuverPayload.maneuverType`.\n */\n maneuver: string\n /** Length of this step in meters. */\n distanceMeters: number\n}\n\nexport type NavLocationPayload = {\n lat: number\n lng: number\n /** Horizontal accuracy in meters, if reported by the platform. */\n accuracy: number | null\n /** Unix ms timestamp of the fix. */\n timestamp: number\n phone_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void\n captions_tester_incident: (event: CaptionsTesterIncidentEvent) => void\n}\n\nexport type ChangeEventPayload = {\n value: string\n}\n\nexport type NavManeuverPayload = {\n /**\n * Categorical type of the upcoming maneuver. One of: STRAIGHT,\n * SLIGHT_LEFT, SLIGHT_RIGHT, TURN_LEFT, TURN_RIGHT, SHARP_LEFT,\n * SHARP_RIGHT, U_TURN, ARRIVE.\n */\n maneuverType: string\n /** Distance in meters from the user's current position to that maneuver. -1 if unknown. */\n distanceMeters: number\n /** Road the user is currently on, per the Nav SDK. Null if unavailable. */\n fromRoad?: string | null\n /**\n * Legacy \"next road\" field — historically populated from the same\n * source as `fromRoad` (the current step's road, NOT the road\n * after the upcoming turn). Kept for back-compat. New consumers\n * should read `nextStepRoad` instead.\n */\n toRoad?: string | null\n /**\n * Road the user will be on AFTER the upcoming maneuver, sourced\n * from the Nav SDK's `remainingSteps[0]`. This is the value to\n * show as the \"next street\" headline on the maneuver card. Null\n * when the SDK hasn't surfaced a remaining step yet (final leg,\n * pre-first-NavInfo, etc.).\n */\n nextStepRoad?: string | null\n /** Total remaining distance to final destination, meters. -1 if unknown. */\n distanceToDestinationMeters?: number\n /** Total remaining travel time, seconds. -1 if unknown. */\n timeToDestinationSeconds?: number\n /** Current speed in m/s. Null if unavailable. */\n currentSpeedMps?: number | null\n /** Speed limit on the current road segment in m/s. Null if unknown / not regulated. */\n speedLimitMps?: number | null\n /** Bearing along the route at the user's current position, 0–360. Null if unknown. */\n routeHeadingDeg?: number | null\n}\n\nexport type NavErrorPayload = {\n message: string\n}\n\nexport type InstalledApp = {\n packageName: string\n appName: string\n isBlocked: boolean\n icon: string | null\n}\n\nexport type PhoneNotificationEvent = {\n notificationId: string\n app: string\n title: string\n content: string\n /**\n * Android notification priority: PRIORITY_MIN (-2) through PRIORITY_MAX (2).\n * Derived from the channel importance when the ranking is available, since\n * Notification.priority reads 0 for channel-based notifications.\n */\n priority: number\n timestamp: number\n packageName: string\n}\n\nexport type PhoneNotificationDismissedEvent = {\n notificationKey: string\n packageName: string\n notificationId: string\n}\n\nexport type CaptionsTesterIncidentEvent = {\n action?: string\n timestamp?: number\n failure_code?: string\n failure_message?: string\n test_run_id?: string\n scenario_name?: string\n source?: string\n [key: string]: unknown\n}\n\nexport type CrustViewProps = {\n url: string\n onLoad: (event: {nativeEvent: OnLoadEventPayload}) => void\n style?: StyleProp<ViewStyle>\n}\n"]}
1
+ {"version":3,"file":"Crust.types.js","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {StyleProp, ViewStyle} from \"react-native\"\n\nexport type OnLoadEventPayload = {\n url: string\n}\n\nexport type CrustModuleEvents = {\n onChange: (params: ChangeEventPayload) => void\n onNavManeuver: (params: NavManeuverPayload) => void\n onNavRerouting: (params: Record<string, never>) => void\n onNavArrived: (params: Record<string, never>) => void\n onNavError: (params: NavErrorPayload) => void\n onNavLocation: (params: NavLocationPayload) => void\n onNavRoute: (params: NavRoutePayload) => void\n onNavOffRoute: (params: NavOffRoutePayload) => void\n onHeading: (params: HeadingPayload) => void\n phone_lock_state: (params: PhoneLockState) => void\n}\n\n/**\n * Whether the user can see the phone right now. iOS derives `unlocked` from\n * protected-data availability, Android from screen on + keyguard hidden.\n * Without a passcode iOS never protects data, so `unlocked` is always true\n * there and carries no information; check `passcodeSet` first.\n */\nexport type PhoneLockState = {\n unlocked: boolean\n passcodeSet: boolean\n}\n\nexport type NavOffRoutePayload = {\n /** Approximate perpendicular distance in meters from the route. */\n offRouteDistanceMeters: number\n}\n\nexport type HeadingPayload = {\n /** Compass heading in degrees, 0 = north, 90 = east. */\n degrees: number\n}\n\nexport type NavRoutePayload = {\n points: Array<{lat: number; lng: number}>\n /**\n * Ordered step list along the route, when supplied by the host. Each\n * step is one navigable segment — typically one straight stretch of\n * road ending in a maneuver. Optional for back-compat with older\n * hosts that only ship the polyline. The SDK's pivot module consumes\n * this to enrich pivots with `fromRoad` / `toRoad` metadata.\n */\n steps?: NavRouteStepPayload[]\n}\n\nexport type NavRouteStepPayload = {\n /** Coordinate where this step begins (== end of the previous step). */\n lat: number\n lng: number\n /**\n * Index into `NavRoutePayload.points[]` where this step starts.\n * Lets consumers correlate step boundaries with polyline geometry.\n */\n routeIndex: number\n /** Name of the road traversed during this step. Null when the engine has no name. */\n road?: string | null\n /**\n * Categorical maneuver performed at the END of this step (i.e. how\n * the user exits this step). Same vocabulary as `NavManeuverPayload.maneuverType`.\n */\n maneuver: string\n /** Length of this step in meters. */\n distanceMeters: number\n}\n\nexport type NavLocationPayload = {\n lat: number\n lng: number\n /** Horizontal accuracy in meters, if reported by the platform. */\n accuracy: number | null\n /** Unix ms timestamp of the fix. */\n timestamp: number\n phone_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void\n captions_tester_incident: (event: CaptionsTesterIncidentEvent) => void\n}\n\nexport type ChangeEventPayload = {\n value: string\n}\n\nexport type NavManeuverPayload = {\n /**\n * Categorical type of the upcoming maneuver. One of: STRAIGHT,\n * SLIGHT_LEFT, SLIGHT_RIGHT, TURN_LEFT, TURN_RIGHT, SHARP_LEFT,\n * SHARP_RIGHT, U_TURN, ARRIVE.\n */\n maneuverType: string\n /** Distance in meters from the user's current position to that maneuver. -1 if unknown. */\n distanceMeters: number\n /** Road the user is currently on, per the Nav SDK. Null if unavailable. */\n fromRoad?: string | null\n /**\n * Legacy \"next road\" field — historically populated from the same\n * source as `fromRoad` (the current step's road, NOT the road\n * after the upcoming turn). Kept for back-compat. New consumers\n * should read `nextStepRoad` instead.\n */\n toRoad?: string | null\n /**\n * Road the user will be on AFTER the upcoming maneuver, sourced\n * from the Nav SDK's `remainingSteps[0]`. This is the value to\n * show as the \"next street\" headline on the maneuver card. Null\n * when the SDK hasn't surfaced a remaining step yet (final leg,\n * pre-first-NavInfo, etc.).\n */\n nextStepRoad?: string | null\n /** Total remaining distance to final destination, meters. -1 if unknown. */\n distanceToDestinationMeters?: number\n /** Total remaining travel time, seconds. -1 if unknown. */\n timeToDestinationSeconds?: number\n /** Current speed in m/s. Null if unavailable. */\n currentSpeedMps?: number | null\n /** Speed limit on the current road segment in m/s. Null if unknown / not regulated. */\n speedLimitMps?: number | null\n /** Bearing along the route at the user's current position, 0–360. Null if unknown. */\n routeHeadingDeg?: number | null\n}\n\nexport type NavErrorPayload = {\n message: string\n}\n\nexport type InstalledApp = {\n packageName: string\n appName: string\n isBlocked: boolean\n icon: string | null\n}\n\nexport type PhoneNotificationEvent = {\n notificationId: string\n app: string\n title: string\n content: string\n /**\n * Android notification priority: PRIORITY_MIN (-2) through PRIORITY_MAX (2).\n * Derived from the channel importance when the ranking is available, since\n * Notification.priority reads 0 for channel-based notifications.\n */\n priority: number\n timestamp: number\n packageName: string\n}\n\nexport type PhoneNotificationDismissedEvent = {\n notificationKey: string\n packageName: string\n notificationId: string\n}\n\nexport type CaptionsTesterIncidentEvent = {\n action?: string\n timestamp?: number\n failure_code?: string\n failure_message?: string\n test_run_id?: string\n scenario_name?: string\n source?: string\n [key: string]: unknown\n}\n\nexport type CrustViewProps = {\n url: string\n onLoad: (event: {nativeEvent: OnLoadEventPayload}) => void\n style?: StyleProp<ViewStyle>\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { NativeModule } from "expo";
2
- import type { CrustModuleEvents, InstalledApp } from "./Crust.types";
2
+ import type { CrustModuleEvents, InstalledApp, PhoneLockState } from "./Crust.types";
3
3
  declare class CrustModule extends NativeModule<CrustModuleEvents> {
4
4
  PI: number;
5
5
  hello(): string;
@@ -27,6 +27,8 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
27
27
  hasNotificationListenerPermission(): Promise<boolean>;
28
28
  refreshNotificationListener(): Promise<boolean>;
29
29
  openNotificationListenerSettings(): Promise<boolean>;
30
+ /** Current lock state; changes arrive as `phone_lock_state` events. */
31
+ getPhoneLockState(): Promise<PhoneLockState>;
30
32
  isBetaBuild(): Promise<boolean>;
31
33
  showLocationServicesDialog(): Promise<boolean>;
32
34
  isLocationServicesEnabled(): Promise<boolean>;
@@ -154,7 +156,7 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
154
156
  * with the same packageName is killed first. Returns true if the
155
157
  * polyfill bundle + miniapp source evaluated without throwing.
156
158
  *
157
- * The polyfill bundle is `mobile/modules/glasslyjs-runtime/dist/startup.js`
159
+ * The polyfill bundle is `mobile/modules/jspolyfill/dist/startup.js`
158
160
  * (shipped inside the host binary). It installs window-style globals
159
161
  * (console, timers, fetch, localStorage, crypto) atop the JSC runtime.
160
162
  */
@@ -174,7 +176,7 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
174
176
  /** Diagnostic — force a GC cycle on the named context. */
175
177
  jsDebugForceGC(packageName: string): Promise<boolean>;
176
178
  /**
177
- * Read the bundled GlasslyJS polyfill (startup.js) shipped inside the
179
+ * Read the bundled JSRuntime polyfill (startup.js) shipped inside the
178
180
  * host binary. Synchronous — host RN code calls this once on app boot
179
181
  * and caches the string, then passes it to every jsSpawn so
180
182
  * every JSContext starts with the same polyfill ABI.
@@ -1 +1 @@
1
- {"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,mBAAmB,EAAC,MAAM,MAAM,CAAA;AAgMtD,yDAAyD;AACzD,eAAe,mBAAmB,CAAc,OAAO,CAAC,CAAA","sourcesContent":["import {NativeModule, requireNativeModule} from \"expo\"\n\nimport type {CrustModuleEvents, InstalledApp} from \"./Crust.types\"\n\ndeclare class CrustModule extends NativeModule<CrustModuleEvents> {\n PI: number\n hello(): string\n setValueAsync(value: string): Promise<void>\n nativeHttpRequest(\n method: string,\n url: string,\n headers: Record<string, string>,\n body?: string | null,\n ): Promise<{status: number; statusText: string; headers: Record<string, string>; body: string}>\n showAVRoutePicker(tintColor?: string | null): void\n\n /**\n * iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an\n * edge is deferred, the first swipe across that edge is consumed by the\n * app and the system gesture (Control Center, Notification Center, Home\n * indicator) only fires on a second swipe — i.e. a two-swipe-to-exit UX.\n *\n * Pass `[]` to restore default behavior. Android: no-op (Android has no\n * per-app equivalent; system gestures are configured at the OS level).\n */\n setDeferredSystemGestures(edges: Array<\"top\" | \"bottom\" | \"left\" | \"right\" | \"all\">): Promise<void>\n\n // Glassly Notification Commands\n setNotificationConfig(listenerEnabled: boolean, blocklist: string[]): Promise<void>\n getInstalledApps(): Promise<InstalledApp[]>\n getInstalledAppsForNotifications(): Promise<InstalledApp[]>\n hasNotificationListenerPermission(): Promise<boolean>\n refreshNotificationListener(): Promise<boolean>\n openNotificationListenerSettings(): Promise<boolean>\n isBetaBuild(): Promise<boolean>\n // location services commands\n showLocationServicesDialog(): Promise<boolean>\n isLocationServicesEnabled(): Promise<boolean>\n openLocationSettings(): Promise<boolean>\n openAppSettings(): Promise<boolean>\n openBluetoothSettings(): Promise<boolean>\n\n // Image Processing Commands\n processGalleryImage(\n inputPath: string,\n outputPath: string,\n options: {\n lensCorrection?: boolean\n colorCorrection?: boolean\n },\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n mergeHdrBrackets(\n underPath: string,\n normalPath: string,\n overPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n stabilizeVideo(\n inputPath: string,\n imuPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n // Media Library Commands\n saveToGalleryWithDate(\n filePath: string,\n captureTimeMillis?: number,\n displayName?: string,\n ): Promise<{\n success: boolean\n uri?: string\n identifier?: string\n existing?: boolean\n error?: string\n }>\n\n // Navigation (both platforms via Mapbox; the dev toggles below are Android-only)\n /**\n * Override the Mapbox access token the native Navigation SDK\n * authenticates with. A non-empty token wins over the one baked at build\n * time (AndroidManifest `com.mapbox.token` / Info.plist `MBXAccessToken`);\n * \"\" clears the override and restores the baked token. Call before\n * `startNavigation` — the engine pushes the user's Settings > Keys token\n * here per trip start.\n */\n setMapboxAccessToken(token: string): Promise<{ok: boolean; error?: string}>\n startNavigation(\n lat: number,\n lng: number,\n options?: {\n simulate?: boolean\n speedMultiplier?: number\n /** Optional multi-stop list. When present takes precedence over lat/lng. Last entry is the final destination. */\n stops?: Array<{lat: number; lng: number}>\n /** \"walking\" | \"driving\" | \"cycling\" | \"two_wheeler\". Defaults driving. */\n mode?: string\n avoid?: {highways?: boolean; tolls?: boolean; ferries?: boolean}\n /** Force a reroute when the user is more than N meters past a pivot they didn't take. */\n missedTurnRerouteMeters?: number\n },\n ): Promise<{ok: boolean; error?: string}>\n stopNavigation(): Promise<{ok: boolean; error?: string}>\n\n /**\n * Kept for API parity with the Google-era contract. Mapbox has no\n * Terms & Conditions dialog, so no dialog is shown; resolves with\n * `{accepted: true}` once permission handling completes. Idempotent —\n * still safe (and intended) to call eagerly.\n */\n requestNavigationPermission(): Promise<{ok: boolean; accepted: boolean; error?: string}>\n\n /**\n * Dev-only: push the simulated user position off-route so the Nav SDK\n * reroutes. Only meaningful in simulate mode. Both platforms;\n * `offsetMeters` tunes the jump on Android and is ignored on iOS.\n */\n simulateDeviation(offsetMeters?: number): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager shifts every\n * reported location ~8m perpendicular to the route bearing, simulating\n * a pedestrian walking on the wrong sidewalk. Only meaningful in\n * simulate mode; lets us verify the SDK's along-path pivot trigger\n * fires even when the user never crosses the 7m pivot point radius.\n * Android-only today; iOS is a no-op stub.\n */\n setWrongSidewalkOffset(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager takes over\n * from the Mapbox simulator and walks the user along a modified\n * polyline that omits crossing micro-steps — reproducing the\n * wrong-sidewalk-then-missed-the-turn scenario. Android-only today;\n * iOS is a no-op stub.\n */\n setSkipCrossings(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n // Heading / compass (both platforms)\n startHeading(): Promise<{ok: boolean; error?: string}>\n stopHeading(): Promise<{ok: boolean; error?: string}>\n\n // GlasslyJS Runtime — per-miniapp JSContext lifecycle.\n /**\n * Spawn a per-miniapp JS context. Re-spawn is allowed: a live context\n * with the same packageName is killed first. Returns true if the\n * polyfill bundle + miniapp source evaluated without throwing.\n *\n * The polyfill bundle is `mobile/modules/glasslyjs-runtime/dist/startup.js`\n * (shipped inside the host binary). It installs window-style globals\n * (console, timers, fetch, localStorage, crypto) atop the JSC runtime.\n */\n jsSpawn(packageName: string, polyfillBundle: string, miniappJs: string): Promise<boolean>\n jsEvaluate(packageName: string, source: string): Promise<unknown>\n jsKill(packageName: string): Promise<void>\n /**\n * Push a `{kind: \"event\"|\"response\", …}` envelope into the named\n * context's globalThis.__deliver. Returns when the underlying\n * evaluateScript completes (the JS handler runs synchronously on the\n * context's queue).\n */\n jsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void>\n jsSetManifest(packageName: string, permissions: string[]): Promise<void>\n /** Diagnostic — returns the packageNames of every live JSContext. */\n jsAlivePackages(): string[]\n /** Diagnostic — force a GC cycle on the named context. */\n jsDebugForceGC(packageName: string): Promise<boolean>\n /**\n * Read the bundled GlasslyJS polyfill (startup.js) shipped inside the\n * host binary. Synchronous — host RN code calls this once on app boot\n * and caches the string, then passes it to every jsSpawn so\n * every JSContext starts with the same polyfill ABI.\n */\n jsLoadPolyfillBundle(): string\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<CrustModule>(\"Crust\")\n"]}
1
+ {"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAE,mBAAmB,EAAC,MAAM,MAAM,CAAA;AAkMtD,yDAAyD;AACzD,eAAe,mBAAmB,CAAc,OAAO,CAAC,CAAA","sourcesContent":["import {NativeModule, requireNativeModule} from \"expo\"\n\nimport type {CrustModuleEvents, InstalledApp, PhoneLockState} from \"./Crust.types\"\n\ndeclare class CrustModule extends NativeModule<CrustModuleEvents> {\n PI: number\n hello(): string\n setValueAsync(value: string): Promise<void>\n nativeHttpRequest(\n method: string,\n url: string,\n headers: Record<string, string>,\n body?: string | null,\n ): Promise<{status: number; statusText: string; headers: Record<string, string>; body: string}>\n showAVRoutePicker(tintColor?: string | null): void\n\n /**\n * iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an\n * edge is deferred, the first swipe across that edge is consumed by the\n * app and the system gesture (Control Center, Notification Center, Home\n * indicator) only fires on a second swipe — i.e. a two-swipe-to-exit UX.\n *\n * Pass `[]` to restore default behavior. Android: no-op (Android has no\n * per-app equivalent; system gestures are configured at the OS level).\n */\n setDeferredSystemGestures(edges: Array<\"top\" | \"bottom\" | \"left\" | \"right\" | \"all\">): Promise<void>\n\n // Glassly Notification Commands\n setNotificationConfig(listenerEnabled: boolean, blocklist: string[]): Promise<void>\n getInstalledApps(): Promise<InstalledApp[]>\n getInstalledAppsForNotifications(): Promise<InstalledApp[]>\n hasNotificationListenerPermission(): Promise<boolean>\n refreshNotificationListener(): Promise<boolean>\n openNotificationListenerSettings(): Promise<boolean>\n /** Current lock state; changes arrive as `phone_lock_state` events. */\n getPhoneLockState(): Promise<PhoneLockState>\n isBetaBuild(): Promise<boolean>\n // location services commands\n showLocationServicesDialog(): Promise<boolean>\n isLocationServicesEnabled(): Promise<boolean>\n openLocationSettings(): Promise<boolean>\n openAppSettings(): Promise<boolean>\n openBluetoothSettings(): Promise<boolean>\n\n // Image Processing Commands\n processGalleryImage(\n inputPath: string,\n outputPath: string,\n options: {\n lensCorrection?: boolean\n colorCorrection?: boolean\n },\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n mergeHdrBrackets(\n underPath: string,\n normalPath: string,\n overPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n stabilizeVideo(\n inputPath: string,\n imuPath: string,\n outputPath: string,\n ): Promise<{\n success: boolean\n outputPath?: string\n processingTimeMs?: number\n error?: string\n }>\n\n // Media Library Commands\n saveToGalleryWithDate(\n filePath: string,\n captureTimeMillis?: number,\n displayName?: string,\n ): Promise<{\n success: boolean\n uri?: string\n identifier?: string\n existing?: boolean\n error?: string\n }>\n\n // Navigation (both platforms via Mapbox; the dev toggles below are Android-only)\n /**\n * Override the Mapbox access token the native Navigation SDK\n * authenticates with. A non-empty token wins over the one baked at build\n * time (AndroidManifest `com.mapbox.token` / Info.plist `MBXAccessToken`);\n * \"\" clears the override and restores the baked token. Call before\n * `startNavigation` — the engine pushes the user's Settings > Keys token\n * here per trip start.\n */\n setMapboxAccessToken(token: string): Promise<{ok: boolean; error?: string}>\n startNavigation(\n lat: number,\n lng: number,\n options?: {\n simulate?: boolean\n speedMultiplier?: number\n /** Optional multi-stop list. When present takes precedence over lat/lng. Last entry is the final destination. */\n stops?: Array<{lat: number; lng: number}>\n /** \"walking\" | \"driving\" | \"cycling\" | \"two_wheeler\". Defaults driving. */\n mode?: string\n avoid?: {highways?: boolean; tolls?: boolean; ferries?: boolean}\n /** Force a reroute when the user is more than N meters past a pivot they didn't take. */\n missedTurnRerouteMeters?: number\n },\n ): Promise<{ok: boolean; error?: string}>\n stopNavigation(): Promise<{ok: boolean; error?: string}>\n\n /**\n * Kept for API parity with the Google-era contract. Mapbox has no\n * Terms & Conditions dialog, so no dialog is shown; resolves with\n * `{accepted: true}` once permission handling completes. Idempotent —\n * still safe (and intended) to call eagerly.\n */\n requestNavigationPermission(): Promise<{ok: boolean; accepted: boolean; error?: string}>\n\n /**\n * Dev-only: push the simulated user position off-route so the Nav SDK\n * reroutes. Only meaningful in simulate mode. Both platforms;\n * `offsetMeters` tunes the jump on Android and is ignored on iOS.\n */\n simulateDeviation(offsetMeters?: number): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager shifts every\n * reported location ~8m perpendicular to the route bearing, simulating\n * a pedestrian walking on the wrong sidewalk. Only meaningful in\n * simulate mode; lets us verify the SDK's along-path pivot trigger\n * fires even when the user never crosses the 7m pivot point radius.\n * Android-only today; iOS is a no-op stub.\n */\n setWrongSidewalkOffset(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev toggle. When enabled, the native NavigationManager takes over\n * from the Mapbox simulator and walks the user along a modified\n * polyline that omits crossing micro-steps — reproducing the\n * wrong-sidewalk-then-missed-the-turn scenario. Android-only today;\n * iOS is a no-op stub.\n */\n setSkipCrossings(enabled: boolean): Promise<{ok: boolean; error?: string}>\n\n // Heading / compass (both platforms)\n startHeading(): Promise<{ok: boolean; error?: string}>\n stopHeading(): Promise<{ok: boolean; error?: string}>\n\n // JS Runtime — per-miniapp JSContext lifecycle.\n /**\n * Spawn a per-miniapp JS context. Re-spawn is allowed: a live context\n * with the same packageName is killed first. Returns true if the\n * polyfill bundle + miniapp source evaluated without throwing.\n *\n * The polyfill bundle is `mobile/modules/jspolyfill/dist/startup.js`\n * (shipped inside the host binary). It installs window-style globals\n * (console, timers, fetch, localStorage, crypto) atop the JSC runtime.\n */\n jsSpawn(packageName: string, polyfillBundle: string, miniappJs: string): Promise<boolean>\n jsEvaluate(packageName: string, source: string): Promise<unknown>\n jsKill(packageName: string): Promise<void>\n /**\n * Push a `{kind: \"event\"|\"response\", …}` envelope into the named\n * context's globalThis.__deliver. Returns when the underlying\n * evaluateScript completes (the JS handler runs synchronously on the\n * context's queue).\n */\n jsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void>\n jsSetManifest(packageName: string, permissions: string[]): Promise<void>\n /** Diagnostic — returns the packageNames of every live JSContext. */\n jsAlivePackages(): string[]\n /** Diagnostic — force a GC cycle on the named context. */\n jsDebugForceGC(packageName: string): Promise<boolean>\n /**\n * Read the bundled JSRuntime polyfill (startup.js) shipped inside the\n * host binary. Synchronous — host RN code calls this once on app boot\n * and caches the string, then passes it to every jsSpawn so\n * every JSContext starts with the same polyfill ABI.\n */\n jsLoadPolyfillBundle(): string\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<CrustModule>(\"Crust\")\n"]}
@@ -20,6 +20,10 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
20
20
  hasNotificationListenerPermission(): Promise<boolean>;
21
21
  refreshNotificationListener(): Promise<boolean>;
22
22
  openNotificationListenerSettings(): Promise<boolean>;
23
+ getPhoneLockState(): Promise<{
24
+ unlocked: boolean;
25
+ passcodeSet: boolean;
26
+ }>;
23
27
  isBetaBuild(): Promise<boolean>;
24
28
  setMapboxAccessToken(_token: string): Promise<{
25
29
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"CrustModule.web.d.ts","sourceRoot":"","sources":["../src/CrustModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAC,MAAM,MAAM,CAAA;AAEpD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAEpD,cAAM,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACvD,EAAE,SAAU;IACN,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;IAS1G,KAAK;IAGL,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IACtC,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1D,qBAAqB,CAAC,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IACrF,gBAAgB;IAGhB,gCAAgC;IAGhC,iCAAiC;IAGjC,2BAA2B;IAG3B,gCAAgC;IAGhC,WAAW;IAGX,oBAAoB,CAAC,MAAM,EAAE,MAAM;;;;IAGnC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAG3D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAGrC,MAAM,CAAC,IAAI,EAAE,MAAM;IAGnB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAG1D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAGlD,eAAe;IAGT,cAAc,CAAC,IAAI,EAAE,MAAM;IAGjC,oBAAoB;CAGrB;;AAED,wBAA4D"}
1
+ {"version":3,"file":"CrustModule.web.d.ts","sourceRoot":"","sources":["../src/CrustModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAC,MAAM,MAAM,CAAA;AAEpD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAEpD,cAAM,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACvD,EAAE,SAAU;IACN,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;IAS1G,KAAK;IAGL,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IACtC,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1D,qBAAqB,CAAC,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IACrF,gBAAgB;IAGhB,gCAAgC;IAGhC,iCAAiC;IAGjC,2BAA2B;IAG3B,gCAAgC;IAGhC,iBAAiB;;;;IAGjB,WAAW;IAGX,oBAAoB,CAAC,MAAM,EAAE,MAAM;;;;IAGnC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAG3D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAGrC,MAAM,CAAC,IAAI,EAAE,MAAM;IAGnB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAG1D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAGlD,eAAe;IAGT,cAAc,CAAC,IAAI,EAAE,MAAM;IAGjC,oBAAoB;CAGrB;;AAED,wBAA4D"}
@@ -34,6 +34,9 @@ class CrustModule extends NativeModule {
34
34
  async openNotificationListenerSettings() {
35
35
  return false;
36
36
  }
37
+ async getPhoneLockState() {
38
+ return { unlocked: true, passcodeSet: false };
39
+ }
37
40
  async isBetaBuild() {
38
41
  return false;
39
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CrustModule.web.js","sourceRoot":"","sources":["../src/CrustModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,MAAM,CAAA;AAIpD,MAAM,WAAY,SAAQ,YAA+B;IACvD,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;IACZ,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAC,KAAK,EAAC,CAAC,CAAA;IAChC,CAAC;IACD,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,GAAW,EAAE,OAA+B,EAAE,IAAoB;QACxG,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA;QAC1D,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACvD,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE;SAC5B,CAAA;IACH,CAAC;IACD,KAAK;QACH,OAAO,iBAAiB,CAAA;IAC1B,CAAC;IACD,iBAAiB,CAAC,UAA0B,IAAG,CAAC;IAChD,KAAK,CAAC,yBAAyB,CAAC,MAAgB,IAAkB,CAAC;IACnE,KAAK,CAAC,qBAAqB,CAAC,gBAAyB,EAAE,UAAoB,IAAkB,CAAC;IAC9F,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,gCAAgC;QACpC,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,iCAAiC;QACrC,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,2BAA2B;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,gCAAgC;QACpC,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,WAAW;QACf,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,oBAAoB,CAAC,MAAc;QACvC,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAC,CAAA;IACnD,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,SAAiB,EAAE,UAAkB;QAC/D,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,IAAY;QACzC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,OAAM;IACR,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,IAA6B;QAC9D,OAAM;IACR,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,MAAgB;QAChD,OAAM;IACR,CAAC;IACD,eAAe;QACb,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,oBAAoB;QAClB,OAAO,EAAE,CAAA;IACX,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA","sourcesContent":["import {registerWebModule, NativeModule} from \"expo\"\n\nimport type {CrustModuleEvents} from \"./Crust.types\"\n\nclass CrustModule extends NativeModule<CrustModuleEvents> {\n PI = Math.PI\n async setValueAsync(value: string): Promise<void> {\n this.emit(\"onChange\", {value})\n }\n async nativeHttpRequest(method: string, url: string, headers: Record<string, string>, body?: string | null) {\n const response = await fetch(url, {method, headers, body})\n return {\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body: await response.text(),\n }\n }\n hello() {\n return \"Hello world! 👋\"\n }\n showAVRoutePicker(_tintColor?: string | null) {}\n async setDeferredSystemGestures(_edges: string[]): Promise<void> {}\n async setNotificationConfig(_listenerEnabled: boolean, _blocklist: string[]): Promise<void> {}\n async getInstalledApps() {\n return []\n }\n async getInstalledAppsForNotifications() {\n return []\n }\n async hasNotificationListenerPermission() {\n return false\n }\n async refreshNotificationListener() {\n return false\n }\n async openNotificationListenerSettings() {\n return false\n }\n async isBetaBuild() {\n return false\n }\n async setMapboxAccessToken(_token: string) {\n return {ok: false, error: \"Not supported on web\"}\n }\n async jsSpawn(_pkg: string, _polyfill: string, _miniappJs: string) {\n return false\n }\n async jsEvaluate(_pkg: string, _src: string) {\n return null\n }\n async jsKill(_pkg: string) {\n return\n }\n async jsDispatchToJs(_pkg: string, _env: Record<string, unknown>) {\n return\n }\n async jsSetManifest(_pkg: string, _perms: string[]) {\n return\n }\n jsAlivePackages() {\n return []\n }\n async jsDebugForceGC(_pkg: string) {\n return false\n }\n jsLoadPolyfillBundle() {\n return \"\"\n }\n}\n\nexport default registerWebModule(CrustModule, \"CrustModule\")\n"]}
1
+ {"version":3,"file":"CrustModule.web.js","sourceRoot":"","sources":["../src/CrustModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,MAAM,CAAA;AAIpD,MAAM,WAAY,SAAQ,YAA+B;IACvD,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;IACZ,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAC,KAAK,EAAC,CAAC,CAAA;IAChC,CAAC;IACD,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,GAAW,EAAE,OAA+B,EAAE,IAAoB;QACxG,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA;QAC1D,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACvD,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE;SAC5B,CAAA;IACH,CAAC;IACD,KAAK;QACH,OAAO,iBAAiB,CAAA;IAC1B,CAAC;IACD,iBAAiB,CAAC,UAA0B,IAAG,CAAC;IAChD,KAAK,CAAC,yBAAyB,CAAC,MAAgB,IAAkB,CAAC;IACnE,KAAK,CAAC,qBAAqB,CAAC,gBAAyB,EAAE,UAAoB,IAAkB,CAAC;IAC9F,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,gCAAgC;QACpC,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,iCAAiC;QACrC,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,2BAA2B;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,gCAAgC;QACpC,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,iBAAiB;QACrB,OAAO,EAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAC,CAAA;IAC7C,CAAC;IACD,KAAK,CAAC,WAAW;QACf,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,oBAAoB,CAAC,MAAc;QACvC,OAAO,EAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAC,CAAA;IACnD,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,SAAiB,EAAE,UAAkB;QAC/D,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,IAAY;QACzC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,OAAM;IACR,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,IAA6B;QAC9D,OAAM;IACR,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,MAAgB;QAChD,OAAM;IACR,CAAC;IACD,eAAe;QACb,OAAO,EAAE,CAAA;IACX,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IACD,oBAAoB;QAClB,OAAO,EAAE,CAAA;IACX,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA","sourcesContent":["import {registerWebModule, NativeModule} from \"expo\"\n\nimport type {CrustModuleEvents} from \"./Crust.types\"\n\nclass CrustModule extends NativeModule<CrustModuleEvents> {\n PI = Math.PI\n async setValueAsync(value: string): Promise<void> {\n this.emit(\"onChange\", {value})\n }\n async nativeHttpRequest(method: string, url: string, headers: Record<string, string>, body?: string | null) {\n const response = await fetch(url, {method, headers, body})\n return {\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body: await response.text(),\n }\n }\n hello() {\n return \"Hello world! 👋\"\n }\n showAVRoutePicker(_tintColor?: string | null) {}\n async setDeferredSystemGestures(_edges: string[]): Promise<void> {}\n async setNotificationConfig(_listenerEnabled: boolean, _blocklist: string[]): Promise<void> {}\n async getInstalledApps() {\n return []\n }\n async getInstalledAppsForNotifications() {\n return []\n }\n async hasNotificationListenerPermission() {\n return false\n }\n async refreshNotificationListener() {\n return false\n }\n async openNotificationListenerSettings() {\n return false\n }\n async getPhoneLockState() {\n return {unlocked: true, passcodeSet: false}\n }\n async isBetaBuild() {\n return false\n }\n async setMapboxAccessToken(_token: string) {\n return {ok: false, error: \"Not supported on web\"}\n }\n async jsSpawn(_pkg: string, _polyfill: string, _miniappJs: string) {\n return false\n }\n async jsEvaluate(_pkg: string, _src: string) {\n return null\n }\n async jsKill(_pkg: string) {\n return\n }\n async jsDispatchToJs(_pkg: string, _env: Record<string, unknown>) {\n return\n }\n async jsSetManifest(_pkg: string, _perms: string[]) {\n return\n }\n jsAlivePackages() {\n return []\n }\n async jsDebugForceGC(_pkg: string) {\n return false\n }\n jsLoadPolyfillBundle() {\n return \"\"\n }\n}\n\nexport default registerWebModule(CrustModule, \"CrustModule\")\n"]}
package/ios/Crust.podspec CHANGED
@@ -51,7 +51,7 @@ Pod::Spec.new do |s|
51
51
 
52
52
  s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
53
53
 
54
- # Ship the GlasslyJS polyfill bundle inside the pod's resource bundle so
54
+ # Ship the JSRuntime polyfill bundle inside the pod's resource bundle so
55
55
  # JSCRuntime can read it at runtime via Bundle.main. The source of
56
56
  # truth lives at `@glassly/jspolyfill/assets/startup.js`, but
57
57
  # CocoaPods silently drops `..` paths from `resource_bundles` — so the
@@ -1,6 +1,7 @@
1
1
  import AVKit
2
2
  import CoreLocation
3
3
  import ExpoModulesCore
4
+ import LocalAuthentication
4
5
  import Photos
5
6
 
6
7
  /// User-visible album in Apple Photos for glasses sync (matches dedicated-folder behavior on Android).
@@ -62,6 +63,19 @@ private struct PhotoLibrarySaveResult {
62
63
  }
63
64
 
64
65
  public class CrustModule: Module {
66
+ private var protectedDataObservers: [NSObjectProtocol] = []
67
+
68
+ /// Phone lock state as seen by the app. Protected data (complete-protection
69
+ /// files) is only readable/writable while the phone is unlocked, which makes
70
+ /// it the standard proxy for "the user can see the phone right now".
71
+ /// Without a passcode iOS never protects data, so `unlocked` is always true
72
+ /// there — callers use `passcodeSet` to know whether the signal means anything.
73
+ private func phoneLockState(unlocked: Bool? = nil) -> [String: Any] {
74
+ let unlocked = unlocked ?? UIApplication.shared.isProtectedDataAvailable
75
+ let passcodeSet = LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
76
+ return ["unlocked": unlocked, "passcodeSet": passcodeSet]
77
+ }
78
+
65
79
  public func definition() -> ModuleDefinition {
66
80
  Name("Crust")
67
81
 
@@ -82,26 +96,50 @@ public class CrustModule: Module {
82
96
  "onNavLocation",
83
97
  "onNavRoute",
84
98
  "onHeading",
85
- // GlasslyJS — fires whenever a per-miniapp JSContext calls
99
+ // Fires on lock / unlock (protected data availability). Payload
100
+ // matches getPhoneLockState().
101
+ "phone_lock_state",
102
+ // JSRuntime — fires whenever a per-miniapp JSContext calls
86
103
  // __dispatch(iface, method, args). RN-side JSRouter
87
104
  // subscribes to route by packageName.
88
- "glasslyjs_message"
105
+ "js_message"
89
106
  )
90
107
 
91
108
  OnCreate {
109
+ // "will become unavailable" fires before isProtectedDataAvailable
110
+ // flips, so the state is taken from which notification fired.
111
+ let center = NotificationCenter.default
112
+ for (name, unlocked) in [
113
+ (UIApplication.protectedDataDidBecomeAvailableNotification, true),
114
+ (UIApplication.protectedDataWillBecomeUnavailableNotification, false),
115
+ ] {
116
+ protectedDataObservers.append(
117
+ center.addObserver(forName: name, object: nil, queue: .main) { [weak self] _ in
118
+ guard let self else { return }
119
+ self.sendEvent("phone_lock_state", self.phoneLockState(unlocked: unlocked))
120
+ }
121
+ )
122
+ }
92
123
  // Wire the JSCRuntime's outbound event sink to the Expo
93
124
  // event emitter. The JSCDispatcher fires `forwardToRn` for
94
125
  // anything that isn't a built-in route (localStorage, fetch,
95
126
  // crypto.getRandomBytes, __runtime.ready), and the runtime's
96
127
  // exception/log/error handlers route through the same sink.
97
128
  JSCRuntime.shared.onOutbound = { [weak self] message in
98
- self?.sendEvent("glasslyjs_message", message.payload)
129
+ self?.sendEvent("js_message", message.payload)
99
130
  }
100
131
  // Install the polyfill bridge once, lazily on first module
101
132
  // creation. JSCPolyfillBridge.install is idempotent.
102
133
  JSCPolyfillBridge.install(into: JSCRuntime.shared.dispatcherTable)
103
134
  }
104
135
 
136
+ OnDestroy {
137
+ for observer in protectedDataObservers {
138
+ NotificationCenter.default.removeObserver(observer)
139
+ }
140
+ protectedDataObservers.removeAll()
141
+ }
142
+
105
143
  Function("hello") {
106
144
  "Hello world! 👋"
107
145
  }
@@ -266,7 +304,11 @@ public class CrustModule: Module {
266
304
  return false
267
305
  }
268
306
 
269
- // MARK: - GlasslyJS Runtime
307
+ AsyncFunction("getPhoneLockState") { () -> [String: Any] in
308
+ await MainActor.run { self.phoneLockState() }
309
+ }
310
+
311
+ // MARK: - JS Runtime
270
312
 
271
313
  /// Spawn a per-miniapp JS context. Re-spawn is allowed: a live
272
314
  /// context with the same packageName is killed first.
@@ -321,7 +363,7 @@ public class CrustModule: Module {
321
363
  return JSCRuntime.shared.debugForceGC(packageName: packageName)
322
364
  }
323
365
 
324
- /// Read the bundled GlasslyJS polyfill (startup.js) from the iOS
366
+ /// Read the bundled JSRuntime polyfill (startup.js) from the iOS
325
367
  /// pod's resource bundle. The host calls this once on app boot,
326
368
  /// caches the string, and passes it to every jsSpawn so
327
369
  /// every JSContext starts with the same polyfill ABI.
@@ -1,4 +1,4 @@
1
- // @generated GlasslyJS polyfill bundle — see mobile/modules/jspolyfill
1
+ // @generated JSRuntime polyfill bundle — see mobile/modules/jspolyfill
2
2
  "use strict";
3
3
  (() => {
4
4
  // src/startup.ts
@@ -424,7 +424,7 @@
424
424
  if (!cryptoNs.subtle) {
425
425
  const notImplemented = () => {
426
426
  throw new Error(
427
- "crypto.subtle is not yet implemented in GlasslyJS \u2014 see agents/glasslyjs-two-layer-miniapp-architecture.md (Polyfill strategy section). Use a pure-JS hash/encrypt library for now."
427
+ "crypto.subtle is not yet implemented in JSRuntime \u2014 see notes/glasslyjs-two-layer-miniapp-architecture.md (Polyfill strategy section). Use a pure-JS hash/encrypt library for now."
428
428
  );
429
429
  };
430
430
  cryptoNs.subtle = new Proxy(
@@ -1,6 +1,6 @@
1
1
  //
2
2
  // JSCDispatcher.swift
3
- // GlasslyJS — dispatch table + permission gate for the per-miniapp
3
+ // JSRuntime — dispatch table + permission gate for the per-miniapp
4
4
  // __dispatch(iface, method, args) bridge.
5
5
  //
6
6
  // Every JSContext spawned by JSCRuntime gets a single Swift block bound
@@ -8,7 +8,7 @@
8
8
  // which:
9
9
  // 1. Looks up the (iface, method) handler from `routes` — local
10
10
  // handlers serve dispatches synchronously, RN-forwarded ones return
11
- // .forwardToRn and the runtime emits an `glasslyjs_message` event.
11
+ // .forwardToRn and the runtime emits a `js_message` event.
12
12
  // 2. Consults the InstalledManifest to gate OS-sensitive calls.
13
13
  // PERMISSION_NOT_DECLARED errors propagate back to JS as thrown
14
14
  // Error objects. There is no JIT permission modal — declared-in-
@@ -33,7 +33,7 @@ public enum JSCDispatchOutcome {
33
33
  case async
34
34
  /// Handler refused; JS sees a thrown Error with `code` + `message`.
35
35
  case error(code: String, message: String?)
36
- /// Forward to RN as `glasslyjs_message`; downstream RN code handles it
36
+ /// Forward to RN as `js_message`; downstream RN code handles it
37
37
  /// and (for request/response calls) eventually calls dispatchToJs back.
38
38
  case forwardToRn([String: Any])
39
39
  }
@@ -153,7 +153,7 @@ public final class JSCDispatcher {
153
153
  // miniapp gets its own UserDefaults suite scoped on packageName.
154
154
  register(iface: "localStorage", method: "getItem") { packageName, args, _ in
155
155
  guard let key = args.first as? String,
156
- let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
156
+ let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
157
157
  return .sync(NSNull())
158
158
  }
159
159
  let v = defaults.string(forKey: key)
@@ -163,7 +163,7 @@ public final class JSCDispatcher {
163
163
  guard args.count >= 2,
164
164
  let key = args[0] as? String,
165
165
  let value = args[1] as? String,
166
- let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
166
+ let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
167
167
  return .error(code: "INVALID_ARGS", message: "localStorage.setItem expects (key, value) strings")
168
168
  }
169
169
  defaults.set(value, forKey: key)
@@ -171,14 +171,14 @@ public final class JSCDispatcher {
171
171
  }
172
172
  register(iface: "localStorage", method: "removeItem") { packageName, args, _ in
173
173
  guard let key = args.first as? String,
174
- let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
174
+ let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
175
175
  return .error(code: "INVALID_ARGS", message: "localStorage.removeItem expects (key)")
176
176
  }
177
177
  defaults.removeObject(forKey: key)
178
178
  return .sync(NSNull())
179
179
  }
180
180
  register(iface: "localStorage", method: "clear") { packageName, _, _ in
181
- guard let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
181
+ guard let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
182
182
  return .sync(NSNull())
183
183
  }
184
184
  for (k, _) in defaults.dictionaryRepresentation() {
@@ -187,14 +187,14 @@ public final class JSCDispatcher {
187
187
  return .sync(NSNull())
188
188
  }
189
189
  register(iface: "localStorage", method: "length") { packageName, _, _ in
190
- guard let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
190
+ guard let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
191
191
  return .sync(0)
192
192
  }
193
193
  return .sync(defaults.dictionaryRepresentation().count)
194
194
  }
195
195
  register(iface: "localStorage", method: "key") { packageName, args, _ in
196
196
  guard let idx = args.first as? Int,
197
- let defaults = UserDefaults(suiteName: "GlasslyJS-\(packageName)") else {
197
+ let defaults = UserDefaults(suiteName: "JSRuntime-\(packageName)") else {
198
198
  return .sync(NSNull())
199
199
  }
200
200
  let keys = Array(defaults.dictionaryRepresentation().keys).sorted()
@@ -1,6 +1,6 @@
1
1
  //
2
2
  // JSCPolyfillBridge.swift
3
- // GlasslyJS — native handlers for the browser-flavoured polyfills that
3
+ // JSRuntime — native handlers for the browser-flavoured polyfills that
4
4
  // can't be implemented in pure JS (fetch, WebSocket).
5
5
  //
6
6
  // Built-in JS shims (console, timers, localStorage, crypto.randomUUID)
@@ -81,7 +81,7 @@ public enum JSCPolyfillBridge {
81
81
  JSCPolyfillBridge.dropSocket(sid: wrapper.sid)
82
82
  }
83
83
  }
84
- private static let log = OSLog(subsystem: "com.tetramo.glassly", category: "GlasslyJS.fetch")
84
+ private static let log = OSLog(subsystem: "com.tetramo.glassly", category: "JSRuntime.fetch")
85
85
 
86
86
  public static func install(into dispatcher: JSCDispatcher) {
87
87
  installFetch(dispatcher)
@@ -1,6 +1,6 @@
1
1
  //
2
2
  // JSCRuntime.swift
3
- // GlasslyJS — per-miniapp JavaScriptCore runtime host (iOS).
3
+ // JSRuntime — per-miniapp JavaScriptCore runtime host (iOS).
4
4
  //
5
5
  // Owns N JSContexts keyed by `packageName`. Each context gets:
6
6
  // - its own JSVirtualMachine (heap isolation)
@@ -16,7 +16,7 @@
16
16
  // jsEvaluate(packageName, src) → Any?
17
17
  // jsKill(packageName) → Void
18
18
  // jsDispatchToJs(packageName, channel, payloadJson) → Void
19
- // Event "glasslyjs_message" — fired when JS calls __dispatch back.
19
+ // Event "js_message" — fired when JS calls __dispatch back.
20
20
  //
21
21
 
22
22
  import Foundation
@@ -31,10 +31,10 @@ public final class JSCRuntime: NSObject {
31
31
  public static let shared = JSCRuntime()
32
32
 
33
33
  /// Tag for os_log lines; visible via Console.app under
34
- /// `subsystem == "com.tetramo.glassly" && category == "GlasslyJS"`.
35
- static let log = OSLog(subsystem: "com.tetramo.glassly", category: "GlasslyJS")
34
+ /// `subsystem == "com.tetramo.glassly" && category == "JSRuntime"`.
35
+ static let log = OSLog(subsystem: "com.tetramo.glassly", category: "JSRuntime")
36
36
 
37
- /// Message emitted back to RN via `Crust.addListener("glasslyjs_message", …)`.
37
+ /// Message emitted back to RN via `Crust.addListener("js_message", …)`.
38
38
  /// `payload` is JSON-friendly so the bridge can ship it verbatim.
39
39
  public struct OutboundMessage {
40
40
  public let packageName: String
@@ -135,7 +135,7 @@ public final class JSCRuntime: NSObject {
135
135
  let str = try? String(contentsOfFile: path, encoding: .utf8) {
136
136
  return str
137
137
  }
138
- os_log("GlasslyJS: polyfill bundle not found in resources", log: Self.log, type: .error)
138
+ os_log("JSRuntime: polyfill bundle not found in resources", log: Self.log, type: .error)
139
139
  return ""
140
140
  }
141
141
 
@@ -153,7 +153,7 @@ public final class JSCRuntime: NSObject {
153
153
  /// - packageName: stable id (e.g. "com.alex.notes"). Must be unique
154
154
  /// within the host process. Re-spawning a live id kills the old
155
155
  /// context first.
156
- /// - polyfillBundle: the contents of `glasslyjs-runtime/dist/startup.js`.
156
+ /// - polyfillBundle: the contents of `jspolyfill/dist/startup.js`.
157
157
  /// Evaluated first, before `miniappJs`.
158
158
  /// - miniappJs: the miniapp's `background/index.js` source. Evaluated
159
159
  /// immediately after the polyfill installs.
@@ -167,9 +167,9 @@ public final class JSCRuntime: NSObject {
167
167
  }
168
168
 
169
169
  let vm = JSVirtualMachine()!
170
- let queue = DispatchQueue(label: "com.tetramo.glasslyjs.\(packageName)", qos: .userInitiated)
170
+ let queue = DispatchQueue(label: "com.tetramo.glassly.jsruntime.\(packageName)", qos: .userInitiated)
171
171
  let ctx = JSContext(virtualMachine: vm)!
172
- ctx.name = "GlasslyJS: \(packageName)"
172
+ ctx.name = "JSRuntime: \(packageName)"
173
173
  #if DEBUG
174
174
  if #available(iOS 16.4, *) {
175
175
  ctx.isInspectable = true
@@ -200,7 +200,7 @@ public final class JSCRuntime: NSObject {
200
200
  if !success {
201
201
  kill(packageName: packageName)
202
202
  } else {
203
- os_log("GlasslyJS: spawned %{public}@", log: Self.log, type: .info, packageName)
203
+ os_log("JSRuntime: spawned %{public}@", log: Self.log, type: .info, packageName)
204
204
  }
205
205
  return success
206
206
  }
@@ -226,7 +226,7 @@ public final class JSCRuntime: NSObject {
226
226
  timer.setEventHandler { [weak self, weak record] in
227
227
  guard let self, let record else { return }
228
228
  let phase = cold ? "cold-start" : "steady-state"
229
- os_log("GlasslyJS NACK: %{public}@ %{public}@ ready signal not received in %.0fs",
229
+ os_log("JSRuntime NACK: %{public}@ %{public}@ ready signal not received in %.0fs",
230
230
  log: Self.log, type: .error,
231
231
  record.packageName, phase, timeoutSeconds)
232
232
  // Surface to RN as a recoverable error frame so the
@@ -282,7 +282,7 @@ public final class JSCRuntime: NSObject {
282
282
  record.context.exception = nil
283
283
  let raw = record.context.evaluateScript(source)
284
284
  if let exception = record.context.exception {
285
- os_log("GlasslyJS [%{public}@] evaluate threw: %{public}@",
285
+ os_log("JSRuntime [%{public}@] evaluate threw: %{public}@",
286
286
  log: Self.log, type: .error,
287
287
  record.packageName, exception.toString() ?? "unknown")
288
288
  record.context.exception = nil
@@ -302,7 +302,7 @@ public final class JSCRuntime: NSObject {
302
302
  public func dispatchToJs(packageName: String, envelope: [String: Any]) {
303
303
  guard let record = lock.withLock({ contexts[packageName] }) else { return }
304
304
  guard let json = Self.jsonString(from: envelope) else {
305
- os_log("GlasslyJS: bad envelope, drop", log: Self.log, type: .error)
305
+ os_log("JSRuntime: bad envelope, drop", log: Self.log, type: .error)
306
306
  return
307
307
  }
308
308
  // Steady-state NACK: re-arm the timer so a wedged JSContext
@@ -338,7 +338,7 @@ public final class JSCRuntime: NSObject {
338
338
  /// timer runs on a dedicated dispatch queue (NOT the per-context
339
339
  /// one) so it can observe the queue being wedged.
340
340
  private static let watchdogScheduler = DispatchQueue(
341
- label: "com.tetramo.glasslyjs.watchdog",
341
+ label: "com.tetramo.glassly.jsruntime.watchdog",
342
342
  qos: .utility,
343
343
  )
344
344
 
@@ -349,7 +349,7 @@ public final class JSCRuntime: NSObject {
349
349
  timer.schedule(deadline: .now() + warn, repeating: .never)
350
350
  timer.setEventHandler { [weak self, weak record] in
351
351
  guard let self, let record else { return }
352
- os_log("GlasslyJS watchdog: %{public}@ %{public}@ blocked >%.0fs",
352
+ os_log("JSRuntime watchdog: %{public}@ %{public}@ blocked >%.0fs",
353
353
  log: Self.log, type: .info,
354
354
  record.packageName, label, warn)
355
355
  // Schedule the kill timer immediately.
@@ -357,7 +357,7 @@ public final class JSCRuntime: NSObject {
357
357
  killTimer.schedule(deadline: .now() + (kill - warn))
358
358
  killTimer.setEventHandler { [weak self, weak record] in
359
359
  guard let self, let record else { return }
360
- os_log("GlasslyJS watchdog: %{public}@ blocked >%.0fs, killing",
360
+ os_log("JSRuntime watchdog: %{public}@ blocked >%.0fs, killing",
361
361
  log: Self.log, type: .error,
362
362
  record.packageName, kill)
363
363
  self.lock.withLock { self._onOutbound }?(
@@ -428,7 +428,7 @@ public final class JSCRuntime: NSObject {
428
428
  // Heap Helper Thread.
429
429
  JSGarbageCollect(record.context.jsGlobalContextRef)
430
430
  }
431
- os_log("GlasslyJS: killed %{public}@", log: Self.log, type: .info, packageName)
431
+ os_log("JSRuntime: killed %{public}@", log: Self.log, type: .info, packageName)
432
432
  }
433
433
 
434
434
  // MARK: - Internals
@@ -592,7 +592,7 @@ public final class JSCRuntime: NSObject {
592
592
  ],
593
593
  )
594
594
  )
595
- os_log("GlasslyJS exception in %{public}@: %{public}@",
595
+ os_log("JSRuntime exception in %{public}@: %{public}@",
596
596
  log: Self.log, type: .error,
597
597
  record.packageName, message)
598
598
  }
@@ -608,7 +608,7 @@ public final class JSCRuntime: NSObject {
608
608
  _ = record.context.evaluateScript(source)
609
609
  if let exception = record.context.exception {
610
610
  let msg = exception.toString() ?? "unknown"
611
- os_log("GlasslyJS [%{public}@] %{public}@ eval threw: %{public}@",
611
+ os_log("JSRuntime [%{public}@] %{public}@ eval threw: %{public}@",
612
612
  log: Self.log, type: .error,
613
613
  record.packageName, label, msg)
614
614
  record.context.exception = nil
@@ -112,15 +112,22 @@ final class NavigationManager: NSObject {
112
112
  applyAccessToken()
113
113
  }
114
114
 
115
- /// Point MapboxOptions.accessToken at the override when set, else restore
116
- /// the baked Info.plist token (the SDK's own boot-time source).
117
- private func applyAccessToken() {
118
- if !accessTokenOverride.isEmpty {
119
- MapboxOptions.accessToken = accessTokenOverride
120
- } else if let baked = Bundle.main.object(forInfoDictionaryKey: "MBXAccessToken") as? String,
121
- !baked.isEmpty {
122
- MapboxOptions.accessToken = baked
115
+ /// The token the next provider build must use: the override when set, else
116
+ /// the baked Info.plist token, else "" (nothing to authenticate with).
117
+ private func resolvedAccessToken() -> String {
118
+ if !accessTokenOverride.isEmpty { return accessTokenOverride }
119
+ if let baked = Bundle.main.object(forInfoDictionaryKey: "MBXAccessToken") as? String, !baked.isEmpty {
120
+ return baked
123
121
  }
122
+ return ""
123
+ }
124
+
125
+ /// Point MapboxOptions.accessToken at the resolved token. Setting the global
126
+ /// alone is NOT enough for routing — see makeProvider — but map/telemetry
127
+ /// components read it, so keep it in step.
128
+ private func applyAccessToken() {
129
+ let token = resolvedAccessToken()
130
+ if !token.isEmpty { MapboxOptions.accessToken = token }
124
131
  }
125
132
 
126
133
  // MARK: - Permissions
@@ -230,7 +237,20 @@ final class NavigationManager: NSObject {
230
237
  let routingConfig = RoutingConfig(
231
238
  rerouteConfig: RerouteConfig(detectsReroute: true)
232
239
  )
240
+ // The token MUST ride on CoreConfig.credentials. The SDK's default
241
+ // credentials read MBXAccessToken from Info.plist (not the runtime
242
+ // MapboxOptions.accessToken), and MapboxNavigationProvider.apply(coreConfig:)
243
+ // then writes that default BACK into MapboxOptions.accessToken. Release
244
+ // builds bake no token by design (the cloud serves it at runtime), so with
245
+ // the default credentials every route request went out unauthenticated
246
+ // and surfaced as "route request failed: … the server returned an empty
247
+ // response". Local builds masked it because the dev .env bakes a token.
248
+ let token = resolvedAccessToken()
249
+ if token.isEmpty {
250
+ print("[NavMgr] no Mapbox token (no runtime override, MBXAccessToken empty) — routing will not authenticate")
251
+ }
233
252
  let coreConfig = CoreConfig(
253
+ credentials: token.isEmpty ? .init() : NavigationCoreApiConfiguration(accessToken: token),
234
254
  routingConfig: routingConfig,
235
255
  locationSource: simulate ? .simulation(initialLocation: nil) : .live
236
256
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/crust",
3
- "version": "0.1.0-dev.99",
3
+ "version": "0.2.8-dev.444",
4
4
  "description": "Glassly Native Module",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -35,7 +35,7 @@
35
35
  "license": "MIT",
36
36
  "homepage": "https://github.com/fossephate/crust#readme",
37
37
  "dependencies": {
38
- "@glassly/jspolyfill": "0.1.0-dev.99"
38
+ "@glassly/jspolyfill": "0.2.8-dev.444"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@expo/config-plugins": ">=8.0.0",
@@ -14,6 +14,18 @@ export type CrustModuleEvents = {
14
14
  onNavRoute: (params: NavRoutePayload) => void
15
15
  onNavOffRoute: (params: NavOffRoutePayload) => void
16
16
  onHeading: (params: HeadingPayload) => void
17
+ phone_lock_state: (params: PhoneLockState) => void
18
+ }
19
+
20
+ /**
21
+ * Whether the user can see the phone right now. iOS derives `unlocked` from
22
+ * protected-data availability, Android from screen on + keyguard hidden.
23
+ * Without a passcode iOS never protects data, so `unlocked` is always true
24
+ * there and carries no information; check `passcodeSet` first.
25
+ */
26
+ export type PhoneLockState = {
27
+ unlocked: boolean
28
+ passcodeSet: boolean
17
29
  }
18
30
 
19
31
  export type NavOffRoutePayload = {
@@ -1,6 +1,6 @@
1
1
  import {NativeModule, requireNativeModule} from "expo"
2
2
 
3
- import type {CrustModuleEvents, InstalledApp} from "./Crust.types"
3
+ import type {CrustModuleEvents, InstalledApp, PhoneLockState} from "./Crust.types"
4
4
 
5
5
  declare class CrustModule extends NativeModule<CrustModuleEvents> {
6
6
  PI: number
@@ -32,6 +32,8 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
32
32
  hasNotificationListenerPermission(): Promise<boolean>
33
33
  refreshNotificationListener(): Promise<boolean>
34
34
  openNotificationListenerSettings(): Promise<boolean>
35
+ /** Current lock state; changes arrive as `phone_lock_state` events. */
36
+ getPhoneLockState(): Promise<PhoneLockState>
35
37
  isBetaBuild(): Promise<boolean>
36
38
  // location services commands
37
39
  showLocationServicesDialog(): Promise<boolean>
@@ -156,13 +158,13 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
156
158
  startHeading(): Promise<{ok: boolean; error?: string}>
157
159
  stopHeading(): Promise<{ok: boolean; error?: string}>
158
160
 
159
- // GlasslyJS Runtime — per-miniapp JSContext lifecycle.
161
+ // JS Runtime — per-miniapp JSContext lifecycle.
160
162
  /**
161
163
  * Spawn a per-miniapp JS context. Re-spawn is allowed: a live context
162
164
  * with the same packageName is killed first. Returns true if the
163
165
  * polyfill bundle + miniapp source evaluated without throwing.
164
166
  *
165
- * The polyfill bundle is `mobile/modules/glasslyjs-runtime/dist/startup.js`
167
+ * The polyfill bundle is `mobile/modules/jspolyfill/dist/startup.js`
166
168
  * (shipped inside the host binary). It installs window-style globals
167
169
  * (console, timers, fetch, localStorage, crypto) atop the JSC runtime.
168
170
  */
@@ -182,7 +184,7 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
182
184
  /** Diagnostic — force a GC cycle on the named context. */
183
185
  jsDebugForceGC(packageName: string): Promise<boolean>
184
186
  /**
185
- * Read the bundled GlasslyJS polyfill (startup.js) shipped inside the
187
+ * Read the bundled JSRuntime polyfill (startup.js) shipped inside the
186
188
  * host binary. Synchronous — host RN code calls this once on app boot
187
189
  * and caches the string, then passes it to every jsSpawn so
188
190
  * every JSContext starts with the same polyfill ABI.
@@ -37,6 +37,9 @@ class CrustModule extends NativeModule<CrustModuleEvents> {
37
37
  async openNotificationListenerSettings() {
38
38
  return false
39
39
  }
40
+ async getPhoneLockState() {
41
+ return {unlocked: true, passcodeSet: false}
42
+ }
40
43
  async isBetaBuild() {
41
44
  return false
42
45
  }