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
@@ -98,6 +98,28 @@ test("with no second argument, a malformed or absent POST body falls back withou
});
});
// GET query strings travel as text (`URLSearchParams` values are always
// strings), so a `v.number()` field must come back as a real number, not the
// string the wire actually carried, or a page declaring `age?: number` on a
// GET api block would be lying about the type. checkField in
// @wrnexus/validation coerces via Number(pre) for both optional and required
// number fields (see packages/validation/src/index.ts); this locks that in
// end-to-end through defineEndpoint's own GET query-string resolution path.
test("a GET request coerces a v.number() query param to an actual number", async () => {
const ageSchema = v.object({ age: v.number() });
const ageEndpoint = defineEndpoint<{ age: number }, { age: number; typeofAge: string }>({
input: ageSchema,
handler(input) {
return { age: input.age, typeofAge: typeof input.age };
},
});
const request = new Request("https://example.test/api/age?age=30");
const ctx = createContext(request, new URL(request.url));
const response = await ageEndpoint(ctx);
expect(response.status).toBe(200);
expect(await response.json()).toEqual({ data: { age: 30, typeofAge: "number" } });
});
test("an explicit rawInput argument still wins and the request is never read", async () => {
// A request whose body has already been consumed: if the endpoint tried to read it
// again (rather than trusting the explicit rawInput), this would throw.