@mentra/crust 3.2.1 → 3.3.0-beta.942
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.
- package/README.md +27 -0
- package/android/build.gradle +15 -9
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/mentra/crust/CrustModule.kt +44 -15
- package/android/src/main/java/com/mentra/crust/http/NativeHttpFailure.kt +31 -0
- package/android/src/main/java/com/mentra/crust/http/NativeHttpRequests.kt +86 -0
- package/android/src/main/java/com/mentra/crust/jsc/JSCPolyfillBridge.kt +1 -25
- package/android/src/main/java/com/mentra/crust/jsc/JSCRuntime.kt +65 -8
- package/android/src/main/java/com/mentra/crust/jsc/JSDeliveryDiagnostics.kt +44 -0
- package/android/src/main/java/com/mentra/crust/navigation/NavigationManager.kt +4 -0
- package/android/src/main/java/com/mentra/crust/preview/PixelCopyPreview.kt +76 -0
- package/android/src/test/java/com/mentra/crust/http/NativeHttpFailureTest.kt +44 -0
- package/android/src/test/java/com/mentra/crust/http/NativeHttpRequestsTest.kt +133 -0
- package/android/src/test/java/com/mentra/crust/jsc/JSDeliveryDiagnosticsTest.kt +54 -0
- package/android/src/test/java/com/mentra/crust/jsc/QuickJsLifecycleTest.kt +87 -0
- package/build/Crust.types.d.ts +25 -11
- package/build/Crust.types.d.ts.map +1 -1
- package/build/Crust.types.js.map +1 -1
- package/build/CrustModule.d.ts +9 -2
- package/build/CrustModule.d.ts.map +1 -1
- package/build/CrustModule.js.map +1 -1
- package/build/CrustModule.web.d.ts +3 -1
- package/build/CrustModule.web.d.ts.map +1 -1
- package/build/CrustModule.web.js +20 -8
- package/build/CrustModule.web.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/nativeHttpRequest.d.ts +13 -0
- package/build/nativeHttpRequest.d.ts.map +1 -0
- package/build/nativeHttpRequest.js +51 -0
- package/build/nativeHttpRequest.js.map +1 -0
- package/build/nativeHttpRequest.test.d.ts +2 -0
- package/build/nativeHttpRequest.test.d.ts.map +1 -0
- package/build/nativeHttpRequest.test.js +88 -0
- package/build/nativeHttpRequest.test.js.map +1 -0
- package/ios/Crust.podspec +1 -1
- package/ios/CrustModule.swift +18 -7
- package/ios/PermissionStatusReceipt.swift +105 -0
- package/package.json +2 -2
- package/plugin/build/index.d.ts +2 -0
- package/plugin/build/index.js +2 -0
- package/plugin/build/withAndroid.js +39 -0
- package/src/Crust.types.ts +26 -12
- package/src/CrustModule.ts +9 -1
- package/src/CrustModule.web.ts +18 -7
- package/src/index.ts +1 -0
- package/src/nativeHttpRequest.test.ts +94 -0
- package/src/nativeHttpRequest.ts +74 -0
- package/android/src/internal/AndroidManifest.xml +0 -9
- package/android/src/internal/java/com/mentra/crust/receivers/CaptionsTesterIncidentReceiver.kt +0 -46
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package com.mentra.crust.preview
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.graphics.Rect
|
|
6
|
+
import android.net.Uri
|
|
7
|
+
import android.os.Handler
|
|
8
|
+
import android.os.Looper
|
|
9
|
+
import android.view.PixelCopy
|
|
10
|
+
import android.view.View
|
|
11
|
+
import java.io.File
|
|
12
|
+
import kotlin.coroutines.resumeWithException
|
|
13
|
+
import kotlinx.coroutines.Dispatchers
|
|
14
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
15
|
+
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
16
|
+
import kotlinx.coroutines.withContext
|
|
17
|
+
|
|
18
|
+
/** Copies the displayed miniapp pixels without invoking WebView's software renderer. */
|
|
19
|
+
object PixelCopyPreview {
|
|
20
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
21
|
+
suspend fun capture(activity: Activity, viewTag: Int): String =
|
|
22
|
+
withContext(Dispatchers.Main.immediate) {
|
|
23
|
+
check(!activity.isFinishing && !activity.isDestroyed) { "Screenshot activity is unavailable" }
|
|
24
|
+
val window = activity.window
|
|
25
|
+
val decor = window.decorView
|
|
26
|
+
val view = decor.findViewById<View>(viewTag)
|
|
27
|
+
?: error("Screenshot view $viewTag is unavailable")
|
|
28
|
+
check(view.isAttachedToWindow && view.isLaidOut && view.width > 0 && view.height > 0) {
|
|
29
|
+
"Screenshot view is not ready"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
val location = IntArray(2)
|
|
33
|
+
view.getLocationInWindow(location)
|
|
34
|
+
val bounds = Rect(location[0], location[1], location[0] + view.width, location[1] + view.height)
|
|
35
|
+
check(Rect(0, 0, decor.width, decor.height).contains(bounds)) {
|
|
36
|
+
"Screenshot view must be fully inside the window"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
val bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888)
|
|
40
|
+
val captured = suspendCancellableCoroutine<Bitmap> { continuation ->
|
|
41
|
+
try {
|
|
42
|
+
PixelCopy.request(window, bounds, bitmap, { result ->
|
|
43
|
+
if (result == PixelCopy.SUCCESS) {
|
|
44
|
+
// A cancelled request still owns its bitmap until PixelCopy finishes.
|
|
45
|
+
continuation.resume(bitmap) { bitmap.recycle() }
|
|
46
|
+
} else {
|
|
47
|
+
bitmap.recycle()
|
|
48
|
+
continuation.resumeWithException(IllegalStateException("PixelCopy failed: $result"))
|
|
49
|
+
}
|
|
50
|
+
}, Handler(Looper.getMainLooper()))
|
|
51
|
+
} catch (error: Exception) {
|
|
52
|
+
bitmap.recycle()
|
|
53
|
+
continuation.resumeWithException(error)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
withContext(Dispatchers.IO) {
|
|
59
|
+
val file = File.createTempFile("miniapp-preview-", ".jpg", activity.cacheDir)
|
|
60
|
+
try {
|
|
61
|
+
file.outputStream().use { output ->
|
|
62
|
+
check(captured.compress(Bitmap.CompressFormat.JPEG, 50, output)) {
|
|
63
|
+
"Unable to encode miniapp screenshot"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
Uri.fromFile(file).toString()
|
|
67
|
+
} catch (error: Exception) {
|
|
68
|
+
file.delete()
|
|
69
|
+
throw error
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} finally {
|
|
73
|
+
captured.recycle()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package com.mentra.crust.http
|
|
2
|
+
|
|
3
|
+
import java.io.IOException
|
|
4
|
+
import java.io.InterruptedIOException
|
|
5
|
+
import java.net.ConnectException
|
|
6
|
+
import java.net.NoRouteToHostException
|
|
7
|
+
import java.net.SocketException
|
|
8
|
+
import java.net.SocketTimeoutException
|
|
9
|
+
import java.net.UnknownHostException
|
|
10
|
+
import javax.net.ssl.SSLHandshakeException
|
|
11
|
+
import org.junit.Assert.assertEquals
|
|
12
|
+
import org.junit.Test
|
|
13
|
+
|
|
14
|
+
class NativeHttpFailureTest {
|
|
15
|
+
@Test fun `preserves failure category without exception text`() {
|
|
16
|
+
val privateMessage = "https://private.example?token=secret SSID private-address"
|
|
17
|
+
val cases = listOf(
|
|
18
|
+
UnknownHostException(privateMessage) to "E_NATIVE_HTTP_DNS",
|
|
19
|
+
ConnectException(privateMessage) to "E_NATIVE_HTTP_CONNECT",
|
|
20
|
+
NoRouteToHostException(privateMessage) to "E_NATIVE_HTTP_CONNECT",
|
|
21
|
+
SocketTimeoutException(privateMessage) to "E_NATIVE_HTTP_TIMEOUT",
|
|
22
|
+
SSLHandshakeException(privateMessage) to "E_NATIVE_HTTP_TLS",
|
|
23
|
+
SocketException(privateMessage) to "E_NATIVE_HTTP_SOCKET",
|
|
24
|
+
InterruptedIOException(privateMessage) to "E_NATIVE_HTTP_INTERRUPTED",
|
|
25
|
+
IOException(privateMessage) to "E_NATIVE_HTTP_IO",
|
|
26
|
+
IllegalStateException(privateMessage) to "E_NATIVE_HTTP_OTHER",
|
|
27
|
+
)
|
|
28
|
+
cases.forEach { (error, expected) -> assertEquals(expected, nativeHttpFailureCode(error)) }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test fun `retains a specific cause inside a generic native wrapper`() {
|
|
32
|
+
val wrapped = IOException("private wrapper", UnknownHostException("private hostname"))
|
|
33
|
+
assertEquals("E_NATIVE_HTTP_DNS", nativeHttpFailureCode(wrapped))
|
|
34
|
+
assertEquals("E_NATIVE_HTTP_IO", nativeHttpFailureCode(IllegalStateException(IOException("private"))))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test fun `never parses a category from private error text and bounds cyclic causes`() {
|
|
38
|
+
assertEquals("E_NATIVE_HTTP_OTHER", nativeHttpFailureCode(Exception("E_NATIVE_HTTP_DNS")))
|
|
39
|
+
val first = Exception("private one")
|
|
40
|
+
val second = Exception("private two", first)
|
|
41
|
+
first.initCause(second)
|
|
42
|
+
assertEquals("E_NATIVE_HTTP_OTHER", nativeHttpFailureCode(first))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
package com.mentra.crust.http
|
|
2
|
+
|
|
3
|
+
import java.io.IOException
|
|
4
|
+
import java.net.ServerSocket
|
|
5
|
+
import java.util.concurrent.CountDownLatch
|
|
6
|
+
import java.util.concurrent.TimeUnit
|
|
7
|
+
import okhttp3.Call
|
|
8
|
+
import okhttp3.Callback
|
|
9
|
+
import okhttp3.OkHttpClient
|
|
10
|
+
import okhttp3.Protocol
|
|
11
|
+
import okhttp3.Request
|
|
12
|
+
import okhttp3.Response
|
|
13
|
+
import okhttp3.ResponseBody.Companion.toResponseBody
|
|
14
|
+
import okio.Timeout
|
|
15
|
+
import org.junit.Assert.*
|
|
16
|
+
import org.junit.Test
|
|
17
|
+
|
|
18
|
+
class NativeHttpRequestsTest {
|
|
19
|
+
private val request = Request.Builder().url("https://example.com/report").build()
|
|
20
|
+
|
|
21
|
+
private class PendingCall(private val request: Request) : Call {
|
|
22
|
+
var callback: Callback? = null
|
|
23
|
+
var cancelled = false
|
|
24
|
+
var onEnqueue: (() -> Unit)? = null
|
|
25
|
+
override fun request() = request
|
|
26
|
+
override fun execute(): Response = error("Test only exercises asynchronous calls")
|
|
27
|
+
override fun enqueue(responseCallback: Callback) {
|
|
28
|
+
callback = responseCallback
|
|
29
|
+
onEnqueue?.invoke()
|
|
30
|
+
}
|
|
31
|
+
override fun cancel() {cancelled = true}
|
|
32
|
+
override fun isExecuted() = callback != null
|
|
33
|
+
override fun isCanceled() = cancelled
|
|
34
|
+
override fun timeout() = Timeout.NONE
|
|
35
|
+
override fun clone(): Call = PendingCall(request)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test fun `register before enqueue and cancel only the requested call`() {
|
|
39
|
+
val calls = mutableListOf<PendingCall>()
|
|
40
|
+
val owner = NativeHttpRequests(Call.Factory { req -> PendingCall(req).also {calls.add(it)} })
|
|
41
|
+
owner.enqueue("one", request, {}, {throw it})
|
|
42
|
+
calls[0].onEnqueue = {assertEquals(2, owner.activeCount)}
|
|
43
|
+
owner.enqueue("two", request, {}, {throw it})
|
|
44
|
+
assertEquals(2, owner.activeCount)
|
|
45
|
+
owner.cancel("one")
|
|
46
|
+
assertTrue(calls[0].cancelled)
|
|
47
|
+
assertFalse(calls[1].cancelled)
|
|
48
|
+
assertEquals(1, owner.activeCount)
|
|
49
|
+
owner.cancel("not-owned")
|
|
50
|
+
assertEquals(1, owner.activeCount)
|
|
51
|
+
owner.cancelAll()
|
|
52
|
+
assertTrue(calls[1].cancelled)
|
|
53
|
+
assertEquals(0, owner.activeCount)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test fun `completion removes the active request and preserves the response`() {
|
|
57
|
+
val call = PendingCall(request)
|
|
58
|
+
val owner = NativeHttpRequests(Call.Factory {call})
|
|
59
|
+
var result: NativeHttpRequests.Result? = null
|
|
60
|
+
call.onEnqueue = {assertEquals(1, owner.activeCount)}
|
|
61
|
+
owner.enqueue("complete", request, {result = it}, {throw it})
|
|
62
|
+
call.callback!!.onResponse(call, Response.Builder().request(request).protocol(Protocol.HTTP_1_1)
|
|
63
|
+
.code(200).message("OK").header("Content-Type", "application/json").body("{}".toResponseBody()).build())
|
|
64
|
+
assertEquals("{}", result!!.body)
|
|
65
|
+
assertEquals("application/json", result!!.headers["content-type"])
|
|
66
|
+
assertEquals(0, owner.activeCount)
|
|
67
|
+
owner.cancel("complete")
|
|
68
|
+
assertFalse(call.cancelled)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test fun `network failure and synchronous enqueue failure leave no registry entries`() {
|
|
72
|
+
val call = PendingCall(request)
|
|
73
|
+
val owner = NativeHttpRequests(Call.Factory {call})
|
|
74
|
+
var failures = 0
|
|
75
|
+
owner.enqueue("failed", request, {}, {failures++})
|
|
76
|
+
call.callback!!.onFailure(call, IOException("offline"))
|
|
77
|
+
assertEquals(0, owner.activeCount)
|
|
78
|
+
call.onEnqueue = {throw IllegalStateException("dispatcher closed")}
|
|
79
|
+
owner.enqueue("enqueue-failed", request, {}, {failures++})
|
|
80
|
+
assertEquals(0, owner.activeCount)
|
|
81
|
+
assertEquals(2, failures)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Test fun `duplicate IDs cannot replace existing ownership and destruction prevents later requests`() {
|
|
85
|
+
val calls = mutableListOf<PendingCall>()
|
|
86
|
+
val owner = NativeHttpRequests(Call.Factory {req -> PendingCall(req).also {calls.add(it)} })
|
|
87
|
+
var failures = 0
|
|
88
|
+
owner.enqueue("one", request, {}, {failures++})
|
|
89
|
+
owner.enqueue("one", request, {}, {failures++})
|
|
90
|
+
assertEquals(1, failures)
|
|
91
|
+
assertEquals(1, owner.activeCount)
|
|
92
|
+
assertNull(calls[1].callback)
|
|
93
|
+
owner.cancelAll()
|
|
94
|
+
assertTrue(calls[0].cancelled)
|
|
95
|
+
owner.enqueue("later", request, {}, {failures++})
|
|
96
|
+
assertEquals(2, failures)
|
|
97
|
+
assertEquals(2, calls.size)
|
|
98
|
+
assertEquals(0, owner.activeCount)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Test fun `cancel interrupts an actual OkHttp response body and closes the server socket`() {
|
|
102
|
+
val client = OkHttpClient()
|
|
103
|
+
val owner = NativeHttpRequests(client)
|
|
104
|
+
val bodyStarted = CountDownLatch(1)
|
|
105
|
+
val socketClosed = CountDownLatch(1)
|
|
106
|
+
val failed = CountDownLatch(1)
|
|
107
|
+
ServerSocket(0, 1, java.net.InetAddress.getLoopbackAddress()).use {server ->
|
|
108
|
+
val thread = Thread {
|
|
109
|
+
server.accept().use {socket ->
|
|
110
|
+
socket.soTimeout = 5_000
|
|
111
|
+
val reader = socket.getInputStream().bufferedReader()
|
|
112
|
+
while (!reader.readLine().isNullOrEmpty()) {}
|
|
113
|
+
socket.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n".toByteArray())
|
|
114
|
+
socket.getOutputStream().flush()
|
|
115
|
+
bodyStarted.countDown()
|
|
116
|
+
if (socket.getInputStream().read() == -1) socketClosed.countDown()
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
thread.isDaemon = true
|
|
120
|
+
thread.start()
|
|
121
|
+
val req = Request.Builder().url("http://127.0.0.1:${server.localPort}/report").build()
|
|
122
|
+
owner.enqueue("stalled-body", req, {fail("Cancelled body must not complete")}, {failed.countDown()})
|
|
123
|
+
assertTrue("server started body", bodyStarted.await(5, TimeUnit.SECONDS))
|
|
124
|
+
owner.cancel("stalled-body")
|
|
125
|
+
assertTrue("native request rejected", failed.await(5, TimeUnit.SECONDS))
|
|
126
|
+
assertTrue("socket closed", socketClosed.await(5, TimeUnit.SECONDS))
|
|
127
|
+
assertEquals(0, owner.activeCount)
|
|
128
|
+
thread.join(5_000)
|
|
129
|
+
}
|
|
130
|
+
client.dispatcher.executorService.shutdownNow()
|
|
131
|
+
client.connectionPool.evictAll()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package com.mentra.crust.jsc
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.*
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class JSDeliveryDiagnosticsTest {
|
|
7
|
+
@Test fun `queue wait and evaluate duration are distinct`() {
|
|
8
|
+
var now = 100L
|
|
9
|
+
val events = mutableListOf<Map<String, Any?>>()
|
|
10
|
+
val diagnostic = JSDeliveryDiagnostics(7, 2, { now }, { events += it })
|
|
11
|
+
diagnostic.entry()
|
|
12
|
+
now = 110
|
|
13
|
+
diagnostic.queued()
|
|
14
|
+
now = 310
|
|
15
|
+
diagnostic.started()
|
|
16
|
+
now = 350
|
|
17
|
+
diagnostic.finished()
|
|
18
|
+
|
|
19
|
+
assertEquals(listOf("native-entry", "queued", "started", "finished"), events.map { it["phase"] })
|
|
20
|
+
assertEquals(200L, events.last()["queueWaitMs"])
|
|
21
|
+
assertEquals(40L, events.last()["evaluateMs"])
|
|
22
|
+
assertEquals(250L, events.last()["elapsedMs"])
|
|
23
|
+
assertEquals(7L, events.last()["deliveryId"])
|
|
24
|
+
assertEquals(2L, events.last()["contextId"])
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Test fun `failure includes exception identity without payload or message`() {
|
|
28
|
+
val events = mutableListOf<Map<String, Any?>>()
|
|
29
|
+
val diagnostic = JSDeliveryDiagnostics(1, 4, { 0L }, { events += it })
|
|
30
|
+
val error = IllegalStateException("private-token user transcript", IllegalArgumentException("private-source"))
|
|
31
|
+
diagnostic.entry()
|
|
32
|
+
diagnostic.queued()
|
|
33
|
+
diagnostic.started()
|
|
34
|
+
diagnostic.finished(error)
|
|
35
|
+
|
|
36
|
+
assertEquals("evaluate-failed", events.last()["phase"])
|
|
37
|
+
assertEquals("IllegalStateException", events.last()["errorClass"])
|
|
38
|
+
assertEquals("IllegalArgumentException", events.last()["causeClass"])
|
|
39
|
+
assertFalse(events.toString().contains("private-"))
|
|
40
|
+
assertFalse(events.toString().contains("transcript"))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Test fun `missing context and rejected executor have explicit terminal events`() {
|
|
44
|
+
for (reason in listOf("context-missing", "executor-rejected")) {
|
|
45
|
+
val events = mutableListOf<Map<String, Any?>>()
|
|
46
|
+
val diagnostic = JSDeliveryDiagnostics(9, null, { 0L }, { events += it })
|
|
47
|
+
diagnostic.entry()
|
|
48
|
+
diagnostic.dropped(reason)
|
|
49
|
+
assertEquals(reason, events.last()["phase"])
|
|
50
|
+
assertNull(events.last()["contextId"])
|
|
51
|
+
assertNull(events.last()["evaluateMs"])
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
package com.mentra.crust.jsc
|
|
2
|
+
|
|
3
|
+
import com.dokar.quickjs.QuickJs
|
|
4
|
+
import com.dokar.quickjs.QuickJsException
|
|
5
|
+
import com.dokar.quickjs.binding.function
|
|
6
|
+
import java.util.concurrent.Executors
|
|
7
|
+
import kotlinx.coroutines.Dispatchers
|
|
8
|
+
import kotlinx.coroutines.runBlocking
|
|
9
|
+
import kotlinx.coroutines.withTimeout
|
|
10
|
+
import org.junit.Assert.*
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Runs the real QuickJS JNI library (through the desktop JVM artifact, see
|
|
15
|
+
* build.gradle) the way [JSCRuntime] uses it: one instance per miniapp, Kotlin
|
|
16
|
+
* bindings called from JS, and independent close orders.
|
|
17
|
+
*/
|
|
18
|
+
class QuickJsLifecycleTest {
|
|
19
|
+
private fun create() = QuickJs.create(Dispatchers.Unconfined)
|
|
20
|
+
|
|
21
|
+
@Test fun `closing one instance keeps another instance's callbacks working`() = runBlocking {
|
|
22
|
+
var calls = 0
|
|
23
|
+
val survivor = create()
|
|
24
|
+
survivor.function("hostCallback") { calls++; 42 }
|
|
25
|
+
val other = create()
|
|
26
|
+
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
27
|
+
other.close()
|
|
28
|
+
withTimeout(1000) { assertEquals(42, survivor.evaluate<Int>("hostCallback()")) }
|
|
29
|
+
assertEquals(2, calls)
|
|
30
|
+
repeat(20) {
|
|
31
|
+
create().close()
|
|
32
|
+
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
33
|
+
}
|
|
34
|
+
val workers = Executors.newFixedThreadPool(4)
|
|
35
|
+
try {
|
|
36
|
+
(1..40).map { workers.submit { create().close() } }.forEach { it.get() }
|
|
37
|
+
assertEquals(42, survivor.evaluate<Int>("hostCallback()"))
|
|
38
|
+
} finally {
|
|
39
|
+
workers.shutdownNow()
|
|
40
|
+
}
|
|
41
|
+
survivor.close()
|
|
42
|
+
val fresh = create()
|
|
43
|
+
fresh.function("hostCallback") { 7 }
|
|
44
|
+
assertEquals(7, fresh.evaluate<Int>("hostCallback()"))
|
|
45
|
+
fresh.close()
|
|
46
|
+
val older = create()
|
|
47
|
+
val newer = create()
|
|
48
|
+
newer.function("hostCallback") { 9 }
|
|
49
|
+
older.close()
|
|
50
|
+
assertEquals(9, newer.evaluate<Int>("hostCallback()"))
|
|
51
|
+
newer.close()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test fun `a binding exception is a catchable JS Error carrying the message`() = runBlocking {
|
|
55
|
+
val qjs = create()
|
|
56
|
+
qjs.function("__dispatch") { throw MentraJSDispatchError("INVALID_ARGS", "iface") }
|
|
57
|
+
val caught = qjs.evaluate<String>(
|
|
58
|
+
"try { __dispatch(); 'no throw' } catch (e) { (e instanceof Error) + '|' + e.message }",
|
|
59
|
+
)
|
|
60
|
+
assertEquals("true|INVALID_ARGS: iface", caught)
|
|
61
|
+
qjs.close()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test fun `non-ASCII strings cross the bridge unchanged`() = runBlocking {
|
|
65
|
+
val text = "héllo 😀 中文 \u0000 end"
|
|
66
|
+
var received: String? = null
|
|
67
|
+
val qjs = create()
|
|
68
|
+
qjs.function("hostText") { text }
|
|
69
|
+
qjs.function("hostEcho") { args -> received = args[0] as String; args[0] }
|
|
70
|
+
assertEquals(text, qjs.evaluate<String>("hostEcho(hostText())"))
|
|
71
|
+
assertEquals(text, received)
|
|
72
|
+
qjs.close()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Test fun `unusual thrown values fail as exceptions, not crashes`() = runBlocking {
|
|
76
|
+
val qjs = create()
|
|
77
|
+
for (source in listOf("throw undefined", "throw null", "throw new Error('%s%n%s%n')")) {
|
|
78
|
+
try {
|
|
79
|
+
qjs.evaluate<Any?>(source)
|
|
80
|
+
fail("$source did not throw")
|
|
81
|
+
} catch (_: QuickJsException) {
|
|
82
|
+
}
|
|
83
|
+
assertEquals(2, qjs.evaluate<Int>("1 + 1"))
|
|
84
|
+
}
|
|
85
|
+
qjs.close()
|
|
86
|
+
}
|
|
87
|
+
}
|
package/build/Crust.types.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export type CrustModuleEvents = {
|
|
|
14
14
|
onHeading: (params: HeadingPayload) => void;
|
|
15
15
|
phone_notification: (event: PhoneNotificationEvent) => void;
|
|
16
16
|
phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void;
|
|
17
|
-
captions_tester_incident: (event: CaptionsTesterIncidentEvent) => void;
|
|
18
17
|
};
|
|
19
18
|
export type NavOffRoutePayload = {
|
|
20
19
|
/** Approximate perpendicular distance in meters from the route. */
|
|
@@ -107,6 +106,8 @@ export type NavManeuverPayload = {
|
|
|
107
106
|
};
|
|
108
107
|
export type NavErrorPayload = {
|
|
109
108
|
message: string;
|
|
109
|
+
/** True only when native navigation has ended; absent for recoverable reroute errors. */
|
|
110
|
+
terminal?: boolean;
|
|
110
111
|
};
|
|
111
112
|
export type InstalledApp = {
|
|
112
113
|
packageName: string;
|
|
@@ -133,16 +134,6 @@ export type PhoneNotificationDismissedEvent = {
|
|
|
133
134
|
packageName: string;
|
|
134
135
|
notificationId: string;
|
|
135
136
|
};
|
|
136
|
-
export type CaptionsTesterIncidentEvent = {
|
|
137
|
-
action?: string;
|
|
138
|
-
timestamp?: number;
|
|
139
|
-
failure_code?: string;
|
|
140
|
-
failure_message?: string;
|
|
141
|
-
test_run_id?: string;
|
|
142
|
-
scenario_name?: string;
|
|
143
|
-
source?: string;
|
|
144
|
-
[key: string]: unknown;
|
|
145
|
-
};
|
|
146
137
|
export type CrustViewProps = {
|
|
147
138
|
url: string;
|
|
148
139
|
onLoad: (event: {
|
|
@@ -150,4 +141,27 @@ export type CrustViewProps = {
|
|
|
150
141
|
}) => void;
|
|
151
142
|
style?: StyleProp<ViewStyle>;
|
|
152
143
|
};
|
|
144
|
+
/** Passive OS observations for the currently executing Mentra App. */
|
|
145
|
+
export type AppPermissionStatus = {
|
|
146
|
+
rawStatus: string | null;
|
|
147
|
+
status: "granted" | "denied" | "blocked" | "unavailable" | "unknown";
|
|
148
|
+
};
|
|
149
|
+
export type AppPermissionSnapshot = {
|
|
150
|
+
schemaVersion: 1;
|
|
151
|
+
observedAt: string;
|
|
152
|
+
identity: {
|
|
153
|
+
pid: number;
|
|
154
|
+
uid: number;
|
|
155
|
+
bundleId: string;
|
|
156
|
+
bundlePath: string;
|
|
157
|
+
executablePath: string;
|
|
158
|
+
version: string;
|
|
159
|
+
build: string;
|
|
160
|
+
};
|
|
161
|
+
locationServicesEnabled: boolean;
|
|
162
|
+
permissions: Record<"location" | "camera" | "microphone" | "bluetooth" | "localNetwork", AppPermissionStatus>;
|
|
163
|
+
};
|
|
164
|
+
export type AppPermissionReceipt = AppPermissionSnapshot & {
|
|
165
|
+
requestId: string;
|
|
166
|
+
};
|
|
153
167
|
//# sourceMappingURL=Crust.types.d.ts.map
|
|
@@ -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;IAC3C,kBAAkB,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC3D,4BAA4B,EAAE,CAAC,KAAK,EAAE,+BAA+B,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"Crust.types.d.ts","sourceRoot":"","sources":["../src/Crust.types.ts"],"names":[],"mappings":"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"}
|
package/build/Crust.types.js.map
CHANGED
|
@@ -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 phone_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void\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_notification: (event: PhoneNotificationEvent) => void\n phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => 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}\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 /** True only when native navigation has ended; absent for recoverable reroute errors. */\n terminal?: boolean\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 CrustViewProps = {\n url: string\n onLoad: (event: {nativeEvent: OnLoadEventPayload}) => void\n style?: StyleProp<ViewStyle>\n}\n\n/** Passive OS observations for the currently executing Mentra App. */\nexport type AppPermissionStatus = {\n rawStatus: string | null\n status: \"granted\" | \"denied\" | \"blocked\" | \"unavailable\" | \"unknown\"\n}\n\nexport type AppPermissionSnapshot = {\n schemaVersion: 1\n observedAt: string\n identity: {\n pid: number\n uid: number\n bundleId: string\n bundlePath: string\n executablePath: string\n version: string\n build: string\n }\n locationServicesEnabled: boolean\n permissions: Record<\"location\" | \"camera\" | \"microphone\" | \"bluetooth\" | \"localNetwork\", AppPermissionStatus>\n}\n\nexport type AppPermissionReceipt = AppPermissionSnapshot & {requestId: string}\n"]}
|
package/build/CrustModule.d.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { NativeModule } from "expo";
|
|
2
|
-
import { CrustModuleEvents, InstalledApp } from "./Crust.types";
|
|
2
|
+
import { AppPermissionReceipt, AppPermissionSnapshot, CrustModuleEvents, InstalledApp } from "./Crust.types";
|
|
3
3
|
declare class CrustModule extends NativeModule<CrustModuleEvents> {
|
|
4
4
|
/** True for the native iOS app running on an Apple Silicon Mac. */
|
|
5
5
|
readonly isIOSAppOnMac?: boolean;
|
|
6
|
+
/** iOS: passive actual-app authorization status; never requests consent. */
|
|
7
|
+
getPermissionStatus(requestId: null): Promise<AppPermissionSnapshot>;
|
|
8
|
+
/** iOS: a nonce publishes fresh status at the fixed managed-cache path. */
|
|
9
|
+
getPermissionStatus(requestId: string): Promise<AppPermissionReceipt>;
|
|
6
10
|
PI: number;
|
|
7
11
|
hello(): string;
|
|
8
12
|
setValueAsync(value: string): Promise<void>;
|
|
9
|
-
|
|
13
|
+
/** Android: copy the displayed view's window pixels to a temporary JPEG URI. */
|
|
14
|
+
captureMiniappPreview(viewTag: number): Promise<string>;
|
|
15
|
+
nativeHttpRequest(requestId: string, method: string, url: string, headers: Record<string, string>, body?: string | null): Promise<{
|
|
10
16
|
status: number;
|
|
11
17
|
statusText: string;
|
|
12
18
|
headers: Record<string, string>;
|
|
13
19
|
body: string;
|
|
14
20
|
}>;
|
|
21
|
+
cancelNativeHttpRequest(requestId: string): Promise<void>;
|
|
15
22
|
showAVRoutePicker(tintColor?: string | null): void;
|
|
16
23
|
/**
|
|
17
24
|
* iOS: configure `preferredScreenEdgesDeferringSystemGestures`. When an
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAsB,MAAM,MAAM,CAAA;AAEtD,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"CrustModule.d.ts","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"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"}
|
package/build/CrustModule.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"CrustModule.js","sourceRoot":"","sources":["../src/CrustModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,mBAAmB,EAAC,MAAM,MAAM,CAAA;AAwMtD,yDAAyD;AACzD,eAAe,mBAAmB,CAAc,OAAO,CAAC,CAAA","sourcesContent":["import {NativeModule, requireNativeModule} from \"expo\"\n\nimport {AppPermissionReceipt, AppPermissionSnapshot, CrustModuleEvents, InstalledApp} from \"./Crust.types\"\n\ndeclare class CrustModule extends NativeModule<CrustModuleEvents> {\n /** True for the native iOS app running on an Apple Silicon Mac. */\n readonly isIOSAppOnMac?: boolean\n /** iOS: passive actual-app authorization status; never requests consent. */\n getPermissionStatus(requestId: null): Promise<AppPermissionSnapshot>\n /** iOS: a nonce publishes fresh status at the fixed managed-cache path. */\n getPermissionStatus(requestId: string): Promise<AppPermissionReceipt>\n PI: number\n hello(): string\n setValueAsync(value: string): Promise<void>\n /** Android: copy the displayed view's window pixels to a temporary JPEG URI. */\n captureMiniappPreview(viewTag: number): Promise<string>\n nativeHttpRequest(\n requestId: string,\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 cancelNativeHttpRequest(requestId: string): Promise<void>\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 // MentraOS 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 (Android only — iOS stubs return error)\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 * Show the Google Nav SDK Terms & Conditions dialog if not already\n * accepted. Idempotent — resolves immediately with `{accepted: true}`\n * when the user has already accepted (cached in-process / on-disk /\n * inside the SDK).\n */\n requestNavigationPermission(): Promise<{ok: boolean; accepted: boolean; error?: string}>\n\n /**\n * Dev-only: clear cached T&C acceptance (SDK flag + on-disk pref +\n * in-process flag) so the next requestNavigationPermission() re-shows\n * the dialog. Android only; iOS returns {ok: false, error: \"not\n * supported on iOS\"}.\n */\n resetNavigationPermission(): Promise<{ok: boolean; error?: string}>\n\n /**\n * Dev-only: nudge the simulated user position ~offsetMeters perpendicular\n * to the route so the Nav SDK reroutes. Default 20m. Android only.\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 Google 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 (Android only)\n startHeading(): Promise<{ok: boolean; error?: string}>\n stopHeading(): Promise<{ok: boolean; error?: string}>\n\n // MentraJS 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/mentrajs-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 mentraJsSpawn(packageName: string, polyfillBundle: string, miniappJs: string): Promise<boolean>\n mentraJsEvaluate(packageName: string, source: string): Promise<unknown>\n mentraJsKill(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 mentraJsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void>\n mentraJsSetManifest(packageName: string, permissions: string[]): Promise<void>\n /** Diagnostic — returns the packageNames of every live JSContext. */\n mentraJsAlivePackages(): string[]\n /** Diagnostic — force a GC cycle on the named context. */\n mentraJsDebugForceGC(packageName: string): Promise<boolean>\n /**\n * Read the bundled MentraJS 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 mentraJsSpawn so\n * every JSContext starts with the same polyfill ABI.\n */\n mentraJsLoadPolyfillBundle(): string\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<CrustModule>(\"Crust\")\n"]}
|
|
@@ -2,8 +2,9 @@ import { NativeModule } from "expo";
|
|
|
2
2
|
import { CrustModuleEvents } from "./Crust.types";
|
|
3
3
|
declare class CrustModule extends NativeModule<CrustModuleEvents> {
|
|
4
4
|
PI: number;
|
|
5
|
+
private readonly httpRequests;
|
|
5
6
|
setValueAsync(value: string): Promise<void>;
|
|
6
|
-
nativeHttpRequest(method: string, url: string, headers: Record<string, string>, body?: string | null): Promise<{
|
|
7
|
+
nativeHttpRequest(requestId: string, method: string, url: string, headers: Record<string, string>, body?: string | null): Promise<{
|
|
7
8
|
status: number;
|
|
8
9
|
statusText: string;
|
|
9
10
|
headers: {
|
|
@@ -11,6 +12,7 @@ declare class CrustModule extends NativeModule<CrustModuleEvents> {
|
|
|
11
12
|
};
|
|
12
13
|
body: string;
|
|
13
14
|
}>;
|
|
15
|
+
cancelNativeHttpRequest(requestId: string): Promise<void>;
|
|
14
16
|
hello(): string;
|
|
15
17
|
showAVRoutePicker(_tintColor?: string | null): void;
|
|
16
18
|
setDeferredSystemGestures(_edges: string[]): Promise<void>;
|
|
@@ -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,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAE/C,cAAM,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACvD,EAAE,SAAU;
|
|
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,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAE/C,cAAM,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACvD,EAAE,SAAU;IACZ,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;IAC5D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG3C,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;;;;IAevH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/D,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,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAGjE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAG3C,YAAY,CAAC,IAAI,EAAE,MAAM;IAGzB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAGhE,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;IAGxD,qBAAqB;IAGf,oBAAoB,CAAC,IAAI,EAAE,MAAM;IAGvC,0BAA0B;CAG3B;;AAED,wBAA4D"}
|