KsContext
Meta-annotation that opts an annotation class into ksrpc's per-call coroutine-context propagation. Apply this to an annotation class together with a binding referencing a concrete KsContextBinding implementation to declare that the annotated KsMethod (or every KsMethod inside an annotated KsService) should propagate a kotlin.coroutines.CoroutineContext.Element value across the wire.
The binding supplies:
A stable wire-level key (KsContextBinding.wireKey) used by transport layers to encode the value.
The kotlin.coroutines.CoroutineContext.Key identity itself — KsContextBinding extends kotlin.coroutines.CoroutineContext.Key, so handlers find the propagated value via the standard
coroutineContext[SomeBinding]lookup naming the binding directly.String encode / decode functions (KsContextBinding.toWire / KsContextBinding.fromWire) used by transport layers that carry the context value as a textual representation.
Compiler validation:
The plugin rejects a
@KsContext-meta-annotated annotation whose binding does not implement KsContextBinding.The plugin rejects a KsMethod (or its enclosing KsService) when two
@KsContext-meta-annotated annotations apply whose bindings declare the same KsContextBinding.wireKey.
Code emission for stub-side put-into-context and handler-side read-from-coroutine-context, plus per-transport wire formats, is handled by follow-up work and is intentionally not part of this annotation's contract.
Samples
// A KsContextBinding pairs a CoroutineContext.Element with a wire key.
// Declare the binding as a named companion on the element type.
val wireKey = RequestId.Key.wireKey // "x-request-id"
// Round-trip: encode to wire, decode from wire
val original = RequestId("abc-123")
val encoded = RequestId.Key.toWire(original)
val decoded = RequestId.Key.fromWire(encoded)// @KsContext is a meta-annotation applied to your own annotation.
// Apply the annotation at the service level (all methods) or per-method.
//
// @Authorized -- applied to SecureService (all methods get it)
// @WithRequestId -- applied to whoAmI only
//
// The compiler plugin validates that bindings implement KsContextBinding
// and that no two bindings share the same wireKey on a single method.// Server handler reads context from the coroutine context.
val service = object : SecureService {
override suspend fun whoAmI(input: String): String {
val auth = coroutineContext[AuthorizationToken.Key]
val reqId = coroutineContext[RequestId.Key]
return "auth=${auth?.bearer}, reqId=${reqId?.value}"
}
}
val env = ksrpcEnvironment { }
val serialized = service.serialized(env)
val stub = serialized.toStub<SecureService, String>()
// Client installs context elements using standard withContext.
// The ksrpc runtime propagates bound elements across the wire.
withContext(AuthorizationToken("secret-token") + RequestId("req-42")) {
val result = stub.whoAmI("hello")
}