Files
huskies/frontend/src/api/rpcContract.ts
T

248 lines
6.7 KiB
TypeScript
Raw Normal View History

2026-05-13 07:10:00 +00:00
/**
* Frontend mirror of the Rust typed RPC contract in
* `server/src/crdt_sync/rpc_contract.rs`.
*
* Every typed write method declared on the backend has matching TypeScript
* params/result types here. The `CONTRACT_FIXTURES` table also exposes the
* same canonical example payloads as the Rust `CONTRACT_METHODS` slice — the
* `rpcContract.test.ts` test compares them against the committed
* `rpcContract.snapshot.json` that the Rust test regenerates. If the Rust
* shapes drift from the TS shapes, the snapshot drifts and one side fails in
* CI — surfacing the mismatch as a compile / test error instead of a runtime
* one.
*
* When adding a method on the backend:
* 1. Add the params + result type here.
* 2. Add the entry to `CONTRACT_FIXTURES` with a canonical example.
* 3. Re-run `UPDATE_RPC_CONTRACT_SNAPSHOT=1 cargo test` to refresh
* `rpcContract.snapshot.json`.
*/
// ── Params types ────────────────────────────────────────────────────────────
/** Params for `model.set_preference`. */
export interface SetModelPreferenceParams {
model: string;
}
/** Params for `anthropic.set_api_key`. */
export interface SetAnthropicApiKeyParams {
api_key: string;
}
/** Params for `settings.put_editor`. */
export interface PutEditorParams {
editor_command: string | null;
}
/** Params for `settings.open_file`. */
export interface OpenFileParams {
path: string;
line: number | null;
}
/** Params for `project.open`. */
export interface OpenProjectParams {
path: string;
}
/** Params for `project.forget`. */
export interface ForgetProjectParams {
path: string;
}
/** Payload for `bot_config.save` (and result of `bot_config.get`). */
export interface BotConfigPayload {
transport: string | null;
enabled: boolean | null;
homeserver: string | null;
username: string | null;
password: string | null;
room_ids: string[] | null;
slack_bot_token: string | null;
slack_signing_secret: string | null;
slack_channel_ids: string[] | null;
}
/** Payload for `settings.put_project` (also returned by `settings.get_project`). */
export interface ProjectSettingsPayload {
default_qa: string;
default_coder_model: string | null;
max_coders: number | null;
max_retries: number;
base_branch: string | null;
rate_limit_notifications: boolean;
timezone: string | null;
rendezvous: string | null;
watcher_sweep_interval_secs: number;
watcher_done_retention_secs: number;
}
// ── Result types ────────────────────────────────────────────────────────────
/** Result envelope for write methods that simply succeed or fail. */
export interface OkResult {
ok: boolean;
}
/** Result for `settings.put_editor`. */
export interface EditorSettingsResult {
editor_command: string | null;
}
/** Result for `project.open`. */
export interface OpenProjectResult {
path: string;
}
// ── Method → params/result mapping ──────────────────────────────────────────
/**
* Compile-time mapping from typed RPC method name to its params + result
* shapes. Used by `callTypedRpc` to enforce that callers pass the right
* params and receive the right return type for a method.
*/
export interface TypedRpcMethods {
"model.set_preference": {
params: SetModelPreferenceParams;
result: OkResult;
};
"anthropic.set_api_key": {
params: SetAnthropicApiKeyParams;
result: OkResult;
};
"settings.put_editor": {
params: PutEditorParams;
result: EditorSettingsResult;
};
"settings.open_file": {
params: OpenFileParams;
result: OkResult;
};
"settings.put_project": {
params: ProjectSettingsPayload;
result: ProjectSettingsPayload;
};
"project.open": {
params: OpenProjectParams;
result: OpenProjectResult;
};
"project.close": {
params: Record<string, never>;
result: OkResult;
};
"project.forget": {
params: ForgetProjectParams;
result: OkResult;
};
"bot_config.save": {
params: BotConfigPayload;
result: BotConfigPayload;
};
"chat.cancel": {
params: Record<string, never>;
result: OkResult;
};
}
/** Union of all typed RPC method names declared in the contract. */
export type TypedRpcMethodName = keyof TypedRpcMethods;
// ── Canonical fixtures (mirror of Rust `CONTRACT_METHODS`) ──────────────────
/**
* One canonical example payload per typed RPC method. The shape *must*
* match the corresponding Rust `CONTRACT_METHODS` entry. Drift between this
* table and `rpcContract.snapshot.json` (regenerated by the Rust side) fails
* the `rpcContract.test.ts` snapshot check.
*/
export const CONTRACT_FIXTURES: {
[K in TypedRpcMethodName]: {
params: TypedRpcMethods[K]["params"];
result: TypedRpcMethods[K]["result"];
};
} = {
"model.set_preference": {
params: { model: "claude-sonnet-4-6" },
result: { ok: true },
},
"anthropic.set_api_key": {
params: { api_key: "sk-ant-..." },
result: { ok: true },
},
"settings.put_editor": {
params: { editor_command: "zed" },
result: { editor_command: "zed" },
},
"settings.open_file": {
params: { path: "src/main.rs", line: 42 },
result: { ok: true },
},
"settings.put_project": {
params: {
default_qa: "server",
default_coder_model: null,
max_coders: null,
max_retries: 2,
base_branch: null,
rate_limit_notifications: true,
timezone: null,
rendezvous: null,
watcher_sweep_interval_secs: 60,
watcher_done_retention_secs: 86_400,
},
result: {
default_qa: "server",
default_coder_model: null,
max_coders: null,
max_retries: 2,
base_branch: null,
rate_limit_notifications: true,
timezone: null,
rendezvous: null,
watcher_sweep_interval_secs: 60,
watcher_done_retention_secs: 86_400,
},
},
"project.open": {
params: { path: "/path/to/project" },
result: { path: "/path/to/project" },
},
"project.close": {
params: {},
result: { ok: true },
},
"project.forget": {
params: { path: "/path/to/project" },
result: { ok: true },
},
"bot_config.save": {
params: {
transport: "matrix",
enabled: true,
homeserver: "https://matrix.example",
username: "bot",
password: "secret",
room_ids: ["!room:example"],
slack_bot_token: null,
slack_signing_secret: null,
slack_channel_ids: null,
},
result: {
transport: "matrix",
enabled: true,
homeserver: "https://matrix.example",
username: "bot",
password: "secret",
room_ids: ["!room:example"],
slack_bot_token: null,
slack_signing_secret: null,
slack_channel_ids: null,
},
},
"chat.cancel": {
params: {},
result: { ok: true },
},
};