kkrpc RPC Framework

repository·main·Indexed 41 days ago

https://github.com/kunkunsh/kkrpc

A TypeScript-first RPC framework for cross-runtime communication. Supports browsers, workers, desktop IPC (Electron/Tauri), and message buses like RabbitMQ, Kafka, and Redis. Features a stable Transport<RPCMessage> API with support for JSON and SuperJSON codecs, async iterable streaming, and remote references.

Tokens
130.4K
Snippets
425
Records
540
Agent score
49%

What's inside kkrpc

  1. What is kkRPC

    main
    kkRPC is a framework that enables type-safe Remote Procedure Calls (RPC) between two JavaScript or TypeScript runtimes. It allows you to expose an API on one side and wrap a transport on the other, enabling you to call remote functions, nested methods, properties, and callbacks as if they were local code. The core library is browser-safe, while specific runtime transports are provided via subpath exports.
  2. What is kkRPC?

    main

    kkRPC is a TypeScript-first RPC (Remote Procedure Call) library designed to simplify cross-context communication. It provides a type-safe way to call functions across different execution environments, eliminating the need for manual message parsing and string-based event names.

    Key benefits include:

    • Type Safety: Full TypeScript support for API calls.
    • No Boilerplate: Reduces the need for manual handlers for every method.
    • Autocomplete: Provides IDE support for discovering available APIs.
    • Advanced Features: Supports nested APIs and callbacks.

    It is particularly useful for developers working with:

    • Electron (Renderer to Main process IPC)
    • Tauri
    • Web Workers
    • Iframes
  3. Key Features of kkrpc

    main

    kkrpc is a high-performance RPC framework designed with the following capabilities:

    • Cross-Runtime Support: Works seamlessly across Node.js, Deno, Bun, and the Browser.
    • Full Type-Safety: Provides complete TypeScript inference and autocomplete.
    • Bidirectional Communication: Unlike traditional RPCs (like tRPC), both the client and server can expose and call APIs on each other.
    • Nested API Organization: Supports organizing APIs using nested object structures (e.g., api.math.grade1.add()).
    • Error Preservation: Maintains complete error objects as they cross runtime boundaries.
    • Function Callbacks: Allows passing functions as parameters across the RPC boundary.
    • Transferable Objects: Supports zero-copy transfers for large data, providing 40-100x performance improvements for large binary payloads.
    • Zero Configuration: Requires no code generation or complex setup; simply install and use.
  4. Supported runtime environments for kkrpc

    main

    kkrpc is designed to be highly portable and supports a wide range of JavaScript runtime environments, including:

    • Server-side Runtimes: Node.js, Deno, and Bun.
    • Client-side/Web: Browser (including Web Workers and iframes) and Chrome Extensions (both content and background scripts).
    • Desktop Frameworks: Electron (supporting full IPC and utility processes) and Tauri (via spawning JS runtimes as sidecars).
  5. Pass callbacks as arguments in RPC calls

    main

    kkrpc supports passing functions (callbacks) as arguments in RPC calls. When a client passes a function to a server, the server can execute that function remotely. The server's execution of the callback is awaited, and the result is returned to the server's logic, which eventually flows back to the client.

    In the remote-references-demo example, a worker receives a callback and executes it, returning a value that the client then receives.

    // Client side
    const callbackResult = await api.useCallback(async (value) => `callback:${value}`)
    console.log(callbackResult) // "callback:from-worker"
  6. Use opt-in entries for Streaming and Remote References

    main

    To keep the core kkrpc bundle size minimal, advanced features like async iterable streaming and Comlink-style remote references are moved to dedicated entry points. Instead of importing from the base kkrpc package, use the specific sub-paths to access these capabilities.

    • Streaming: Use kkrpc/streaming to access RPCChannel, wrap, and expose with async iterable streaming support.
    • Remote References: Use kkrpc/remote-refs to access RPCChannel, wrap, expose, and explicit proxy helpers like proxy(), releaseProxy(), and isRemoteProxy().
  7. How the RPCMessage protocol works

    main

    The RPCMessage is the wire-level union type used for all communication. It uses short keys to minimize serialized size, which is particularly beneficial for string-based transports. The protocol supports the following message types:

    • Request (t: "q"): { t: "q", id, op, p, a?, v? } (includes operation, params, arguments, and validation)
    • Response (t: "r"): { t: "r", id, v?, e? } (includes result or error)
    • Callback (t: "cb"): { t: "cb", id, a } (used for server-to-client callbacks)
    • Callback release (t: "cbr"): { t: "cbr", ids } (signals end of callback lifecycle)
    • Stream request (t: "sq"): { t: "sq", id, sid, op, n?, v? } (initiates a stream)
    • Stream response (t: "sr"): { t: "sr", id, sid, d?, v?, e? } (delivers stream data/errors)
  8. How property access works in kkrpc

    main

    kkrpc uses JavaScript Proxy objects to allow direct property access and mutation across RPC boundaries. This makes remote objects behave like local ones with full TypeScript support.

    Key Mechanics

    • Property Getters: Accessing a property (e.g., api.counter) returns a proxy. To retrieve the actual value, you must use await (e.g., await api.counter). This triggers a "get" type RPC message.
    • Property Setters: Assigning a value (e.g., api.counter = 42) triggers the proxy's set trap, sending a "set" type RPC message.
    • Nested Properties: Deep access is supported via dot notation (e.g., api.nested.deep.property).
    • Fire-and-Forget Setters: Property assignments (api.counter = 42) are non-blocking and return immediately. They do not wait for the remote operation to complete.

    Error Handling for Setters

    Because setters are fire-and-forget, a failed assignment will not throw at the site of the assignment. To catch errors from failed sets (kind: "set") or remote-callback failures (kind: "callback"), you must pass an onUncaughtError handler to the channel.

    // Getter (requires await)
    const val = await api.property
    
    // Setter (fire-and-forget)
    api.property = newValue
  9. Core Architecture: RPCChannel and Transport

    main

    The stable architecture of kkrpc is built around two primary abstractions: RPCChannel and Transport.

    • RPCChannel<LocalAPI, RemoteAPI>: Manages the RPC state machine. It handles:
      • pending: Outbound requests waiting for a response.
      • callbacks: Local callback arguments provided for remote execution.
      • destroyed: State to prevent calls after disposal and reject pending requests.
      • timeout: Single call timeout (defaults to 30_000 ms; non-positive values disable it).
      • plugins: Hooks for request, handler, response, and error to implement validation, middleware, or inspection.
    • Transport<T>: A message-oriented interface that decouples IO details from RPC message encoding. It uses createTransport() to combine a Platform<Wire> with a Codec<Message, Wire>.

    When a RPCChannel receives a message, it dispatches it based on the compact field t (type).

  10. Understand kkrpc bundle size configurations

    main

    The benchmark measures the incremental cost of adding specific features to the kkrpc core. Use these configurations to decide which features to include in your project based on your bundle size constraints:

    • kkrpc core: The base package including the generic Transport<RPCMessage> channel, proxy creation, top-level callback arguments, and transfer descriptors.
    • kkrpc + json codec: Core plus createTransport() and jsonCodec() for plain JSON serialization.
    • kkrpc + superjson: Core plus createTransport() and superJsonCodec() for richer JS value serialization.
    • kkrpc/worker: Core plus workerTransport() for Web Worker support.
    • kkrpc/streaming: Core plus opt-in async iterable argument/result streaming with pull-based backpressure.
    • kkrpc/remote-refs: Core plus explicit proxy(value) remote references with nested marked refs and identity behavior.
  11. Preserve error metadata and inheritance in kkrpc

    main

    When an error occurs on the remote side, kkrpc provides enhanced error preservation. This ensures that:

    • Custom error classes maintain their inheritance hierarchy.
    • Error properties and metadata are preserved across the RPC boundary.
    • Error causes and chaining remain intact, allowing for detailed debugging.
    • Validation errors include the necessary context from the remote environment.