Skip to content

Runtime

Transport, message parsing, and routing a protocol call to its implementation all have to happen at run time. Comline provides a runtime that does this, so each project does not reinvent it.

Per-language runtimes

Each language has its own runtime, a normal dependency built and shipped like any other library: comline-runtime for Rust, @comline/runtime for TypeScript. Generated code calls into it; you rarely do.

flowchart TB
    H["your handlers and calls"] --> GC["generated code<br/>types, senders, dispatch"]
    GC -->|Rust| RS["comline-runtime"]
    GC -->|TypeScript| TS["@comline/runtime"]
    TS -->|"WebAssembly,<br/>through the C ABI"| RS
    RS --> TR["transport<br/>TCP, in-memory, yours"]
    TS --> TR

The protocol logic that has to be byte-identical everywhere (framing, request ids, the handshake's frames) is written once, in Rust: comline-runtime is the shared runtime core, and its C ABI exposes it to other languages. The TypeScript runtime loads it as WebAssembly for its built-in framings, and falls back to its own TypeScript implementation where the core can't load (a strict content security policy, say) or for a framing of your own; both paths behave the same and are tested the same. Codecs, generated types and transports stay in each language.

Every runtime is checked against the same conformance vectors, so a Rust peer and a TypeScript peer generated from one schema speak the same bytes.

See Language Guides for the practical, per-language next step: spawning a client, serving a protocol, and the concrete framing and wire-format API. This page stays the conceptual overview.