pakstr 0.31.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. package/README.md +1 -1
  2. package/android-template/app/build.gradle.kts +1 -0
  3. package/android-template/app/src/androidTest/java/com/pakstr/app/PakstrWebBridgeTest.kt +153 -0
  4. package/android-template/app/src/main/assets/nip07.js +124 -74
  5. package/android-template/app/src/main/java/com/pakstr/app/AppOrigin.kt +77 -0
  6. package/android-template/app/src/main/java/com/pakstr/app/PakstrBridge.kt +5 -5
  7. package/android-template/app/src/main/java/com/pakstr/app/PakstrWebBridge.kt +89 -0
  8. package/android-template/app/src/main/java/com/pakstr/app/WebMessageRouter.kt +186 -0
  9. package/android-template/app/src/main/java/com/pakstr/app/WebViewController.kt +65 -49
  10. package/android-template/app/src/main/java/com/pakstr/app/signer/AmberRequestChannel.kt +156 -0
  11. package/android-template/app/src/main/java/com/pakstr/app/signer/AmberSigner.kt +142 -390
  12. package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +69 -489
  13. package/android-template/app/src/main/java/com/pakstr/app/signer/SignerManager.kt +13 -2
  14. package/android-template/app/src/test/java/com/pakstr/app/AppOriginTest.kt +114 -0
  15. package/android-template/app/src/test/java/com/pakstr/app/WebMessageRouterTest.kt +279 -0
  16. package/android-template/app/src/test/java/com/pakstr/app/signer/AmberRequestChannelTest.kt +294 -0
  17. package/android-template/app/src/test/java/com/pakstr/app/signer/NostrBridgeDispatchTest.kt +113 -0
  18. package/android-template/gradle/libs.versions.toml +2 -0
  19. package/package.json +1 -1
@@ -0,0 +1,113 @@
1
+ package com.pakstr.app.signer
2
+
3
+ import com.pakstr.app.signer.interfaces.NostrSigner
4
+ import kotlinx.coroutines.runBlocking
5
+ import org.json.JSONArray
6
+ import org.json.JSONObject
7
+ import org.junit.Assert.assertEquals
8
+ import org.junit.Assert.assertThrows
9
+ import org.junit.Assert.assertTrue
10
+ import org.junit.Test
11
+
12
+ class NostrBridgeDispatchTest {
13
+ private val pubkey = "b".repeat(64)
14
+
15
+ @Test
16
+ fun each_method_reaches_the_matching_signer_call_with_its_arguments() {
17
+ val signer = RecordingSigner()
18
+ val bridge = NostrBridge(signerProvider = { signer })
19
+ val event = JSONObject()
20
+ .put("kind", 1)
21
+ .put("created_at", 1_700_000_000)
22
+ .put("tags", JSONArray().put(JSONArray().put("t").put("x")))
23
+ .put("content", "hi")
24
+
25
+ runBlocking {
26
+ assertEquals("pk", bridge.handle("getPublicKey", null))
27
+ assertEquals("signed", bridge.handle("signEvent", event))
28
+ assertEquals("c04", bridge.handle("nip04_encrypt", JSONObject().put("pubkey", pubkey).put("plaintext", "p")))
29
+ assertEquals("p04", bridge.handle("nip04_decrypt", JSONObject().put("pubkey", pubkey).put("ciphertext", "c")))
30
+ assertEquals("c44", bridge.handle("nip44_encrypt", JSONObject().put("pubkey", pubkey).put("plaintext", "p")))
31
+ assertEquals("p44", bridge.handle("nip44_decrypt", JSONObject().put("pubkey", pubkey).put("ciphertext", "c")))
32
+ }
33
+
34
+ assertEquals(
35
+ listOf(
36
+ listOf("getPublicKey"),
37
+ listOf("signEvent", event.toString()),
38
+ listOf("nip04Encrypt", pubkey, "p"),
39
+ listOf("nip04Decrypt", pubkey, "c"),
40
+ listOf("nip44Encrypt", pubkey, "p"),
41
+ listOf("nip44Decrypt", pubkey, "c")
42
+ ),
43
+ signer.calls
44
+ )
45
+ assertEquals(event.toString(), JSONObject(signer.calls[1][1]).toString())
46
+ }
47
+
48
+ @Test
49
+ fun invalid_arguments_fail_before_the_signer_is_requested() {
50
+ var requested = 0
51
+ val bridge = NostrBridge(signerProvider = { requested++; RecordingSigner() })
52
+
53
+ val cases = listOf(
54
+ Triple("signEvent", null, "Event payload is empty"),
55
+ Triple("nip04_encrypt", null, "NIP04 encrypt payload is empty"),
56
+ Triple("nip04_decrypt", null, "NIP04 decrypt payload is empty"),
57
+ Triple("nip04_encrypt", JSONObject().put("pubkey", pubkey), null),
58
+ Triple("nip44_encrypt", null, null),
59
+ Triple("nip44_decrypt", JSONObject().put("ciphertext", "c"), null)
60
+ )
61
+ for ((method, params, message) in cases) {
62
+ val error = assertThrows(Exception::class.java) {
63
+ runBlocking { bridge.handle(method, params) }
64
+ }
65
+ if (message != null) assertEquals(method, message, error.message)
66
+ }
67
+ assertEquals(0, requested)
68
+ }
69
+
70
+ @Test
71
+ fun unsupported_methods_are_rejected_without_a_signer() {
72
+ var requested = 0
73
+ val bridge = NostrBridge(signerProvider = { requested++; RecordingSigner() })
74
+
75
+ for (method in listOf("", "call", "debugLog", "getPublicKey ", "nip04Encrypt", "__proto__")) {
76
+ val error = assertThrows(UnsupportedBridgeMethodException::class.java) {
77
+ runBlocking { bridge.handle(method, null) }
78
+ }
79
+ assertEquals("Unsupported method", error.message)
80
+ }
81
+ assertEquals(0, requested)
82
+ }
83
+
84
+ @Test
85
+ fun failures_are_logged_and_rethrown_without_payloads() {
86
+ val logs = mutableListOf<String>()
87
+ val bridge = NostrBridge(
88
+ signerProvider = { throw IllegalStateException("boom") },
89
+ log = { logs += it },
90
+ logError = { message, _ -> logs += message }
91
+ )
92
+
93
+ assertThrows(IllegalStateException::class.java) {
94
+ runBlocking {
95
+ bridge.handle("nip44_encrypt", JSONObject().put("pubkey", pubkey).put("plaintext", "secret text"))
96
+ }
97
+ }
98
+
99
+ assertEquals(listOf("Request: nip44_encrypt", "nip44_encrypt failed"), logs)
100
+ assertTrue(logs.none { "secret text" in it })
101
+ }
102
+
103
+ private class RecordingSigner : NostrSigner {
104
+ val calls = mutableListOf<List<String>>()
105
+
106
+ override suspend fun getPublicKey(): String = "pk".also { calls += listOf("getPublicKey") }
107
+ override suspend fun signEvent(eventJson: String): String = "signed".also { calls += listOf("signEvent", eventJson) }
108
+ override suspend fun nip04Encrypt(pubkey: String, plaintext: String) = "c04".also { calls += listOf("nip04Encrypt", pubkey, plaintext) }
109
+ override suspend fun nip04Decrypt(pubkey: String, ciphertext: String) = "p04".also { calls += listOf("nip04Decrypt", pubkey, ciphertext) }
110
+ override suspend fun nip44Encrypt(pubkey: String, plaintext: String) = "c44".also { calls += listOf("nip44Encrypt", pubkey, plaintext) }
111
+ override suspend fun nip44Decrypt(pubkey: String, ciphertext: String) = "p44".also { calls += listOf("nip44Decrypt", pubkey, ciphertext) }
112
+ }
113
+ }
@@ -17,6 +17,7 @@ json = "20250517"
17
17
  okhttp = "4.12.0"
18
18
  securityCrypto = "1.1.0"
19
19
  zxingCore = "3.5.3"
20
+ webkit = "1.18.0"
20
21
 
21
22
  [libraries]
22
23
  androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
@@ -42,6 +43,7 @@ json = { module = "org.json:json", version.ref = "json" }
42
43
  okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
43
44
  androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" }
44
45
  zxing-core = { module = "com.google.zxing:core", version.ref = "zxingCore" }
46
+ androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
45
47
 
46
48
  [plugins]
47
49
  android-application = { id = "com.android.application", version.ref = "agp" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pakstr",
3
- "version": "0.31.0",
3
+ "version": "1.0.1",
4
4
  "description": "CLI for packaging Nostr web apps into Android APKs",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",