Read bodies of responses the engine never recorded

A send with no request behind it — a plugin's ad-hoc ctx.httpRequest.send,
GraphQL introspection — gets a generated id and a body file, but no row.
Resolving purely through the database refused those, which would have
broken auth-oauth2 the moment it moved off readFileSync, since every
request it sends is ad-hoc.

The store now falls back to the response directory when there is no row,
accepting only ids shaped the way the engine generates them. Such a
response has no stored headers, so contentType is null and text()
decodes as UTF-8 — which is what the filesystem readers did anyway.
This commit is contained in:
Gregory Schier
2026-08-16 09:35:03 -07:00
parent 96c8a95094
commit 8b031db685
5 changed files with 89 additions and 26 deletions
@@ -261,11 +261,14 @@ export const plugin: PluginDefinition = {
* The response's body as text, or null when there is nothing to read.
*
* The host is asked for it by response id, so this works wherever the bytes
* happen to live. A body over the runtime's size limit throws rather than
* coming back empty, since a template silently rendering to nothing is worse
* than one that says why.
* happen to live — including responses it never recorded, which still get an
* id. A body over the runtime's size limit throws rather than coming back
* empty, since a template silently rendering to nothing is worse than one that
* says why.
*/
async function readResponseBody(ctx: Context, response: HttpResponse): Promise<string | null> {
// Belt and braces: everything reaching here came from find() or send() and so
// has an id. An empty one would just be an unreadable id.
if (!response.id) return null;
const body = await ctx.httpResponse.body({ responseId: response.id });