Code generation turns a frozen schema into typed client stubs with autocomplete, type checking, and inline doc comments.

Running the Generator

saikuro-codegen --schema schema.json --lang typescript --out ./generated
saikuro-codegen --schema schema.json --lang python    --out ./generated
saikuro-codegen --schema schema.json --lang csharp    --out ./generated
saikuro-codegen --schema schema.json --lang rust      --out ./generated
saikuro-codegen --schema schema.json --lang c         --out ./generated
saikuro-codegen --schema schema.json --lang cpp       --out ./generated

Generate for each language separately:

saikuro-codegen --schema schema.json --lang typescript --out ./generated/typescript
saikuro-codegen --schema schema.json --lang python --out ./generated/python
saikuro-codegen --schema schema.json --lang csharp --out ./generated/csharp

Output Structure

Language Types file Client file per namespace
TypeScript types.ts <Namespace>Client.ts
Python types.py <namespace>_client.py
C# Types.cs <Namespace>Client.cs
Rust types.rs <namespace>_client.rs

Private functions are never emitted. Public and internal functions both appear in generated stubs.

Generated Code Example

Given a schema with a math.add function that takes two i64 and returns an i64:

TypeScript:

// AUTO-GENERATED by saikuro-codegen
import { Client } from "@nisoku/saikuro";

export class MathClient {
  constructor(private readonly client: Client) {}

  /** Add two integers. */
  async add(a: number, b: number): Promise<number> {
    return this.client.call("math.add", [a, b]);
  }
}
import { SaikuroClient } from "@nisoku/saikuro";
import { MathClient } from "./generated/MathClient";

const client = await SaikuroClient.connect("unix:///tmp/saikuro.sock");
const math = new MathClient(client);
const result = await math.add(1, 2);

Python:

# AUTO-GENERATED by saikuro-codegen
from saikuro import Client

class MathClient:
    def __init__(self, client: Client) -> None:
        self._client = client

    async def add(self, a: int, b: int) -> int:
        """Add two integers."""
        return await self._client.call("math.add", [a, b])

C#:

// AUTO-GENERATED by saikuro-codegen
using Saikuro;

public sealed class MathClient {
    private readonly Client _client;
    public MathClient(Client client) => _client = client;

    public Task<long> AddAsync(long a, long b)
        => _client.CallAsync<long>("math.add", new object[] { a, b });
}

Custom Types

Named types in the schema become native types in each language:

Schema TypeScript Python C#
{ kind: "record", fields: { ... } } interface @dataclass record

Build Pipeline

The cleanest approach is to commit the schema to your repo and regenerate stubs as part of your build:

# Extract schema from provider
npx saikuro-schema my-namespace src/provider.ts > schema.json

# Generate stubs for consumer languages
saikuro-codegen --schema schema.json --lang python --out clients/python/generated
saikuro-codegen --schema schema.json --lang csharp --out clients/csharp/Generated

# Generate TypeScript stubs for other TypeScript consumers
saikuro-codegen --schema schema.json --lang typescript --out clients/typescript/generated

Next Steps