Code Generation
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