mirror of
https://github.com/morluto/rea.git
synced 2026-11-05 22:36:08 +00:00
fix(javascript): link a response body reader only to a settled fetch (#8028)
* fix(javascript): link a response body reader only to a settled fetch
`collectResponseConsumers` treated any `.json()`/`.text()`/`.arrayBuffer()` on a
binding holding a request result as a response consumer, and gave it the
request's endpoint at `resolution: "complete"`. That holds only for a settled
`fetch`.
Measured on Node 24.22.1:
await fetch(u) -> Response json/text/arrayBuffer = function
fetch(u) unawaited -> Promise json = undefined; pending.json() THROWS TypeError
http.get(u) -> ClientRequest json/text/arrayBuffer = undefined
http.request(u) -> ClientRequest json = undefined; request.text() THROWS TypeError
new WebSocket(u) -> WebSocket socket.json() THROWS TypeError
await http.get(u) -> ClientRequest (await does not help)
So four shapes emitted a resolved "this endpoint's body is read here" fact for a
call that throws before any body is read: an un-awaited fetch, `http.get`,
`http.request`, and a `WebSocket` handshake.
Link only requests that settle to a `Response`: a `fetch` call whose value is
awaited through expression wrappers. A binding that also holds a request kind
without a body reader keeps the consumer a candidate (`partial`) rather than a
proven one, and a binding with no settled fetch emits no consumer at all, which
matches the `collectReleases` / `forkRejectsStdioArray` precedent — the receiver
provably has no such member, so there is no fact to report.
* chore: retrigger title and readiness checks after the title was corrected
This commit is contained in:
1 parent
a97f827469
commit
d5ba7cec60
2 files changed
+119
-2
No files matched your search
@@ -8,6 +8,7 @@ import type {
|
||||
} from "./javascriptSemanticIr.js";
|
||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||
import {
|
||||
javaScriptExpressionWrapperTarget,
|
||||
readExactJavaScriptLiteral,
|
||||
semanticStaticPropertyKey,
|
||||
semanticStaticPropertyName,
|
||||
@@ -304,6 +305,28 @@ const soleWriteReachesRead = (
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Only `fetch` resolves to a `Response`, and only once its promise settles.
|
||||
* `http.request`/`http.get` return a `ClientRequest` and `new WebSocket` a
|
||||
* socket, neither of which has `json`/`text`/`arrayBuffer`; an un-awaited
|
||||
* `fetch` returns a `Promise`. In all of those the call throws at the call site
|
||||
* instead of reading a body, so it is not a response consumer at all.
|
||||
*/
|
||||
const settlesToResponse = (
|
||||
request: RequestCandidate,
|
||||
context: TraversalContext,
|
||||
): boolean => {
|
||||
if (request.operation.method !== "fetch") return false;
|
||||
let node: t.Node = request.node;
|
||||
for (;;) {
|
||||
const parent = context.state.parentsByNode.get(node);
|
||||
if (parent === undefined) return false;
|
||||
if (t.isAwaitExpression(parent)) return true;
|
||||
if (javaScriptExpressionWrapperTarget(parent) !== node) return false;
|
||||
node = parent;
|
||||
}
|
||||
};
|
||||
|
||||
const collectResponseConsumers = (
|
||||
program: t.Program,
|
||||
context: TraversalContext,
|
||||
@@ -338,11 +361,20 @@ const collectResponseConsumers = (
|
||||
member.object,
|
||||
member.object.name,
|
||||
);
|
||||
const linked =
|
||||
const candidates =
|
||||
binding === undefined
|
||||
? []
|
||||
: (requestsByBinding.get(binding.bindingId) ?? []);
|
||||
// A body reader only exists on a settled fetch Response. Requests of any
|
||||
// other kind throw at this call site instead, so they link nothing; if no
|
||||
// candidate survives, the call is not a response consumer at all.
|
||||
const linked = candidates.filter((request) =>
|
||||
settlesToResponse(request, context),
|
||||
);
|
||||
if (linked.length === 0) return;
|
||||
// A binding that also receives a request kind without a body reader leaves
|
||||
// this reader a candidate, not a proven consumer of the fetch response.
|
||||
const otherKinds = candidates.length !== linked.length;
|
||||
// A consumer of exactly one request through its own result binding
|
||||
// reads that request's response, so it keeps the request's endpoint.
|
||||
const uniqueRequest =
|
||||
@@ -377,7 +409,8 @@ const collectResponseConsumers = (
|
||||
.sort(compareUnicodeCodePoints),
|
||||
endpoint: soleWrite ? (uniqueRequest?.operation.endpoint ?? null) : null,
|
||||
fields: [],
|
||||
resolution: uniqueRequest === undefined ? "partial" : "complete",
|
||||
resolution:
|
||||
uniqueRequest === undefined || otherKinds ? "partial" : "complete",
|
||||
});
|
||||
});
|
||||
return output;
|
||||
|
||||
Reference in new issue
Block a user