Skip to content

Project

Evo

Type-safe, schema-based RPC system for distributed TypeScript applications. Enables validated communication across client, server, and UI contexts....

Description

Type-safe, schema-based RPC system for distributed TypeScript applications. Enables validated communication across client, server, and UI contexts.

Goals

  • Provide a reliable, validated RPC layer across distributed client, server, and UI runtimes.
  • Eliminate runtime bugs using strict schema validation with Zod.
  • Support contract-driven communication methods with full TypeScript safety.
  • Enable retry logic and timeout handling across distributed runtime conditions.

Features

  • Built for distributed runtime environments: Supports communication across client, server, and UI layers with a consistent, contract-driven interface.
  • Contract-driven design: Define RPC methods with Zod schemas and auto-generate fully-typed APIs with runtime validation.
  • Cross-environment communication: Handles bi-directional calls between Client ⇄ Server, Client ⇄ UI, and UI ⇄ Client.
  • Retry and timeout handling: Built-in support for configurable retry strategies, delay enforcement, and timeout safeguards.
  • Runtime environment safety: Prevents incorrect usage by validating the execution context before sending or handling RPC calls.
  • Response routing and cleanup: Uses UUIDs to track and clean up response listeners automatically for better memory and safety control.

Usage

  • // 1. Define a contract using Zod for type-safe args and return values
    import { z } from 'zod';
    import { Contract } from '@smaiill/evo';
    
    const userContract = new Contract({
      getUser: {
        args: z.object({ id: z.number() }),
        returns: z.object({ name: z.string(), email: z.string() }),
      },
    }).build();
  • // 2. Define a server-side RPC listener (RS = server, RC = client)
    userContract.createListener('rs', 'rc', {
      getUser: async ({ id }) => {
        // Simulate user data fetching
        return { name: 'Alice', email: 'alice@example.com' };
      },
    });
  • // 3. Define a client-side API (RC = client, RS = server)
    const api = userContract.createApi('rc', 'rs');
    
    // Make a request to the server from the client
    const user = await api.getUser({ id: 1 });
    // Output: { name: string; email: string }
  • // 4. Add retry options to make the method more resilient
    const contractWithRetry = new Contract({
      getUser: {
        args: z.object({ id: z.number() }),
        returns: z.object({ name: z.string() }),
        retryOptions: {
          max: 3,          // Max 3 attempts
          delay: 1000,     // Wait 1 second between retries
          forceDelay: false,
        },
      },
    }).build();
  • // 5. Global timeout for all API calls in the contract (in ms)
    const contractWithTimeout = new Contract(
      {
        getUser: {
          args: z.object({ id: z.number() }),
          returns: z.object({ name: z.string() }),
        },
      },
      {
        timeout: 5000, // 5 seconds max timeout for RPC calls
      }
    ).build();

Learning outcomes

  • Built a runtime-safe, contract-based RPC system for a multi-environment game framework
  • Applied deep type inference and validation using TypeScript and Zod