fix(javascript): bind response endpoints only from writes that reach the read (#4759)

collectResponseConsumers copied the linked request's endpoint onto a
response consumer whenever the receiver binding had exactly one
initializer (added in 0323ce0a2, #3822). A single initializer is a source
fact, not proof that the write reaches the read: an incoming parameter
that is overwritten only conditionally, or only after the read, still
lets the consumer read a caller-supplied response, yet it received the
request's exact endpoint.

The sole write now also has to cover the read, using the same rule the
call resolver applies to incoming values: either it is the binding's
stable unconditional declaration initializer, or the shared read-position
owner (semanticBindingAtRead) reports a definite preceding initializer.
Otherwise the endpoint stays null.

Fixes #4606

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 authored and GitHub committed 2026-10-17 04:26:55 -07:00
1 parent 31c87b40ee
commit f1ab5959bd
2 files changed
+54 -3

No files matched your search

@@ -57,3 +57,38 @@ it("leaves a response replaced by a non-request value without an endpoint", () =
`),
).toEqual([expect.objectContaining({ method: "json", endpoint: null })]);
});
it.each([
[
"ordered",
`async function load() { const res = await fetch("https://example.invalid/api"); return res.json(); }`,
"https://example.invalid/api",
],
[
"parameter-replaced-before",
`async function load(res) { res = await fetch("https://example.invalid/api"); return res.json(); }`,
"https://example.invalid/api",
],
[
"parameter-conditional",
`async function load(res, maybe) { if (maybe) res = await fetch("https://example.invalid/api"); return res.json(); }`,
null,
],
[
"parameter-later",
`async function load(res) { const value = await res.json(); res = await fetch("https://example.invalid/api"); return value; }`,
null,
],
[
"multiple-requests",
`async function load(maybe) { let res = await fetch("https://example.invalid/a"); if (maybe) res = await fetch("https://example.invalid/b"); return res.json(); }`,
null,
],
] as const)(
"keeps a response endpoint only when its request reaches the read: %s",
(_name, source, endpoint) => {
expect(responses(source)).toEqual([
expect.objectContaining({ method: "json", endpoint }),
]);
},
);