fix(compiler): stop error {} from swallowing response {} bugs in api blocks

Client codegen chained .then().catch(), so a .catch() after .then()
caught exceptions thrown by the response body too. Switched to the
two-argument then(onFulfilled, onRejected) form, whose rejection
handler cannot see errors from the fulfilment handler.

SSR codegen wrapped both the transport call and the response-body eval
in the same try; only __wrnexusCallApi is now inside the try, and
__wrnexusEvalData runs after it, outside.

Also verifies (and locks in with a regression test) that GET query
numbers already coerce correctly through defineEndpoint + checkField,
and documents that in the typed-api-block spec.
This commit is contained in:
2026-08-19 21:11:26 +05:30
parent b5029889a5
commit 3ae5d7cf97
8 changed files with 239 additions and 9 deletions
@@ -167,6 +167,83 @@ test("an ssr block used in {#each} with an error section runs the error body on
expect(html).toContain("fallback");
});
test("an ssr block's response body error is not swallowed by the error section", async () => {
const generated = generate(
parse(`page Repro {
ssr {
api ssrUsers GET /api/users {
response {
return data.users.missing.length
}
error {
return ["fallback"]
}
}
}
view { <main>{#each ssrUsers as u}<p>{u}</p>{/each}</main> }
}
`),
);
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-response-throws-"));
roots.push(root);
mkdirSync(root, { recursive: true });
const file = join(root, "page.ts");
writeFileSync(file, generated);
const mod = await import(`${file.replace(/\\/g, "/")}?t=${Date.now()}`);
await expect(
mod.default({
req: { url: "http://localhost/", headers: new Headers() },
cookies: {},
session: {},
localStorage: {},
__wrnexusCallApi: async () => ({ users: [] }),
}),
).rejects.toThrow();
});
test("an ssr block still runs the error body on a genuine transport failure", async () => {
const generated = generate(
parse(`page Repro {
ssr {
api ssrUsers GET /api/users {
response {
return data.users.length
}
error {
return ["fallback"]
}
}
}
view { <main>{#each ssrUsers as u}<p>{u}</p>{/each}</main> }
}
`),
);
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-transport-fails-"));
roots.push(root);
mkdirSync(root, { recursive: true });
const file = join(root, "page.ts");
writeFileSync(file, generated);
const mod = await import(`${file.replace(/\\/g, "/")}?t=${Date.now()}`);
const html = await mod.default({
req: { url: "http://localhost/", headers: new Headers() },
cookies: {},
session: {},
localStorage: {},
__wrnexusCallApi: async () => {
throw new Error("boom");
},
});
expect(html).toContain("fallback");
});
test("an ssr block used in {#each} without an error section still propagates a failure", async () => {
const generated = generate(
parse(`page Repro {