Fix plugin RPCs with schema transforms (#8550)

* fix(plugins): parse RPC values once at the receiver

Sending parsed values caused transforms to run again on incompatible schema outputs. Keep the wire values in schema-input form and parse only where consumed.

* fix(plugins): let omitted RPC inputs reach schema defaults

* fix(plugins): accept omitted RPC outputs for client defaults
This commit is contained in:
Mohamed Boudra
2026-11-02 15:42:39 +07:00
committed by GitHub
parent 3e3a707f5a
commit 63302287fe
8 changed files with 105 additions and 20 deletions
+16 -10
View File
@@ -24,17 +24,23 @@ describe("plugin RPC contracts", () => {
expectTypeOf<RpcOutput<typeof contract>>().toEqualTypeOf<{ value: number }>();
});
it("validates input before invoking the daemon", async () => {
let invocations = 0;
const invoke = async () => {
invocations += 1;
return { value: 2 };
it("sends input unchanged for the plugin to parse", async () => {
let parses = 0;
const transformed = defineRpc({
name: "transformed",
input: z.string().transform((text) => {
parses += 1;
return Number(text);
}),
output: z.number(),
});
const invoke = async (_method: string, input: unknown) => {
expect(input).toBe("2");
expect(parses).toBe(0);
return transformed.input.parseAsync(input);
};
await expect(callPluginRpc(contract, invoke, { value: "wrong" })).rejects.toBeInstanceOf(
z.ZodError,
);
expect(invocations).toBe(0);
await expect(callPluginRpc(transformed, invoke, "2")).resolves.toBe(2);
expect(parses).toBe(1);
});
it("validates output before resolving", async () => {
+1 -2
View File
@@ -39,8 +39,7 @@ export async function callPluginRpc<InputSchema extends ZodType, OutputSchema ex
invoke: (method: string, input: unknown) => Promise<unknown>,
input: PluginWireInput<ZodInput<InputSchema>>,
): Promise<ZodOutput<OutputSchema>> {
const parsedInput = await contract.input.parseAsync(input);
const output = await invoke(contract.name, parsedInput);
const output = await invoke(contract.name, input);
return contract.output.parseAsync(output);
}