api.global/kotlinsupport
Kotlin clients for api.global TypedRequest and TypedSocket services, with server-derived Kotlin contracts. Native applications use TypedSocketClient for requests and live updates; the shared library owns envelope encoding, connection lifecycle and protocol validation.
TypedSocketClient implements wire major 9, shared by npm TypedSocket 9 and 10: exact version/cancellation handshake, bidirectional typed calls, exact request-instance correlation, server-directed retries, cancellation, bounded text frames, reconnect backoff, application restoration before readiness and client tag restoration. HTTPS/WSS is required except explicitly enabled loopback development. Redirects and shared HTTP caches are disabled. Failed in-flight calls are never replayed after reconnect. Error diagnostics exclude URLs, headers, request contents and response contents; structured server refusals remain available as TypedResponseError.code, serverMessage and data.
Consume
Pin this repository as a Git submodule and include its Android module:
include(":api-client")
project(":api-client").projectDir = file("vendor/api-global-kotlinsupport/android")
The consumer supplies com.android.library on its plugin classpath and depends on project(":api-client"). This checkout builds with AGP 9.4.1, Kotlin 2.4.21, compile SDK 37 and minimum SDK 26. Runtime imports are under global.api.kotlinsupport.
val client = TypedSocketClient(
endpoint = URI("https://example.com/socket"),
parentScope = applicationScope,
headers = { mapOf("Authorization" to credentials.bearerHeader()) },
restoreConnection = { connection ->
connection.fire(SocketContracts.subscribe, SubscribeRequest(topic = "updates"))
},
)
val result = client.fire(SocketContracts.getProfile, GetProfileRequest())
The contract and request names above are generated examples. handle(contract, decodeRequest, encodeResponse, handler) registers server-initiated requests before connecting. status is a StateFlow; start() begins connection, connect() waits for readiness, and stop() ends that client lifecycle. Create a new instance when starting a later lifecycle. terminalCloseCodes lets an application stop permanently on a revocation code; lastCloseCode exposes only its numeric value. Credential preparation failures, incompatible handshakes and nontransport application-restoration failures end the client.
TypedRequestClient provides the same generated-contract calls over the canonical TypedRequest HTTP endpoint for services that explicitly need that transport. It posts protocol envelopes, not REST routes. Native app feature flows should remain on TypedSocket.
Binary VirtualStreams
A generated response carries a server-created stream descriptor. Decode it through the SDK and open it on the same physical connection:
val response = client.fire(SocketContracts.beginDocumentDownload, request)
val descriptor = VirtualStreamDescriptor.fromJson(response.stream.toJson())
val receiver = client.openReceiver(descriptor, maximumBytes = 20_000_000)
try {
while (true) {
val bytes = receiver.receive() ?: break
temporaryFile.write(bytes)
}
commitTemporaryFile() // application-owned durable acceptance
val receipt = receiver.accept()
} catch (error: Exception) {
receiver.reject()
throw error
}
The contract and storage helpers above are application examples. Uploads use openSender(descriptor, maximumBytes), send(ByteArray), then close(). Sender close() resolves only after the receiver application's ACCEPT; FIN alone is insufficient. Cancel using sender abort() or receiver reject(). Receipts include logical chunk count, byte length and whether SHA-256 integrity was verified.
The SDK implements npm TypedSocket 9/10's canonical virtual-stream-v1 binary frames with 32 KiB frame and 4 MiB logical chunk bounds, ordered acknowledgements, optional exact length/SHA-256 proofs, bounded stream/connection queues and a fixed acceptance deadline after drainage. It checks descriptor expiry and direction before opening; server-side capability binding and authorization govern access. A descriptor belongs to its issuing physical connection. Disconnect and stop fail all operations; stream bytes never replay after reconnect. Application upload queues must reconcile an ambiguous result before retrying. Descriptor and error diagnostics redact capabilities and content.
Generate contracts
Use api.global's canonical TypeScript schema pipeline, then the Kotlin generator. Python 3.9 or later is required. pnpm install --frozen-lockfile installs the TypeScript exporter and test server dependencies.
node tools/export-contracts.mjs --config export.config.json
python3 tools/generate_contracts.py --config kotlinsupport.config.json
python3 tools/generate_contracts.py --config kotlinsupport.config.json --check
The exporter config names tsconfig, contracts and out, with paths relative to the config file; optional methods, schemaTypes and serverInitiated select the source contracts. The Kotlin generator accepts that schema set or an OpenAPI document's components.schemas. Keep the schema snapshot, generator config and generated output with each consumer. See contract generation for names, supported shapes, null versus omission, open enums and sealed unions.
Check
python3 -m unittest discover -s test
KOTLINSUPPORT_NODE_HOME="$HOME/.nvm/versions/node/v22.23.1" \
ci/in-docker.sh ./gradlew :android:testDebugUnitTest :android:lintDebug :android:assembleRelease
The wrapper builds the pinned JDK/Android image on first use. KOTLINSUPPORT_NODE_HOME mounts an existing Node 22.18+ installation for real protocol interoperability tests. The verified Node 22 installation runs in this image; Node 26 additionally needs libatomic in the image. Integration tests fail clearly if their server cannot start. Tests use disposable local servers and generated fixtures, never production services.
MIT licensed; see license.md.