Implements the verifiable pure-Kotlin core of the Android client per
docs/ANDROID_CLIENT_PLAN.md, mirroring the iOS SPM package set as Gradle modules.
Built + reviewed via multi-agent workflow (explore→implement→verify→review),
then review findings fixed with regression tests.
Modules (all pure JVM, kotlin("jvm"); Android-framework modules scaffolded but
gated off in settings — no Android SDK in this env):
- :wire-protocol — frozen wire contract (sealed Client/ServerMessage, enums,
HostEndpoint CSWSH origin derivation, transport interfaces), hand-rolled codec
byte-identical to the server's JSON.stringify + tolerant kotlinx decode.
- :session-core — ReconnectMachine (1→2→4→8→16→30 backoff), PingScheduler,
GateTracker (two-line epoch guard), AwayDigest, UnreadLedger, TitleSanitizer,
KeyByteMap (byte-matches public/keybar.ts).
- :api-client — all REST routes, tolerant decode, Origin-iff-guarded, prefs
unknown-key preservation, pairing probe + host-tier classifier.
- :client-tls — pure half: PKCS#12 parse, X509KeyManager alias logic,
CertificateSummary, provider-agnostic wrong-passphrase classification.
- :test-support — FakeTransport / FakeHttpTransport / virtual-clock fakes.
Verify: ./gradlew clean test → 218 tests, 0 failures (wire-protocol 47,
session-core 60, api-client 69, client-tls 27, test-support 15).
Review (3 lenses, 8/10 each) findings fixed: sessionId JSON-injection escape,
CancellationException propagation in PingScheduler, PairingProbe close-on-cancel
leak, HostEndpoint trim, HostClassifier hoisted to :wire-protocol (type unified),
BouncyCastle-portable wrong-passphrase detection, lone-surrogate escaping.
Known gap (documented, deferred): /push/fcm-token has no server route yet —
plan task A33 (src/push/fcm.ts) delivers it.
38 lines
1.6 KiB
TOML
38 lines
1.6 KiB
TOML
## Version catalog — single source of truth for plugin & dependency versions.
|
|
## Pure JVM modules only (this env has NO Android SDK). See android/README.md.
|
|
## Kotlin pinned to 2.3.21 to match the Kotlin embedded in Gradle 9.6.1 (best
|
|
## compatibility with the Kotlin Gradle Plugin on Gradle 9.6).
|
|
|
|
[versions]
|
|
kotlin = "2.3.21"
|
|
kotlinxSerialization = "1.9.0"
|
|
kotlinxCoroutines = "1.10.2"
|
|
junit = "5.13.4"
|
|
junitPlatform = "1.13.4"
|
|
turbine = "1.2.1"
|
|
mockk = "1.14.2"
|
|
|
|
[libraries]
|
|
# Serialization
|
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
|
|
|
|
# Coroutines
|
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
|
|
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
|
|
|
|
# Test — JUnit5 (Jupiter)
|
|
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
|
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junitPlatform" }
|
|
|
|
# Test — Flow assertions + mocking
|
|
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
|
|
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
|
|
|
[bundles]
|
|
# Convenience bundle wired into every pure module's testImplementation.
|
|
unit-test = ["junit-jupiter", "kotlinx-coroutines-test", "turbine", "mockk"]
|
|
|
|
[plugins]
|
|
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|