fix(proxy): preserve fetch semantics through the proxy adapter
Addresses review round 4 on #2099 — three posted comments and three suppressed ones, all valid. - A rebuilt Response reported `url: ""`, `redirected: false`, and `type: "default"` even after undici followed a redirect: a fetch-contract regression visible only to proxy users, and one `core/auth/endpointOverrides.ts` explicitly guards against by declining to rebuild at all. This adapter has no such choice — the response arrives from the wrong realm — so it now reinstates the three with `defineProperty`. - Normalizing a `Request` input dropped every option the Request itself carried beyond method/headers/body/signal. Most visibly `redirect`: a `new Request(url, { redirect: "manual" })` started following redirects instead of surfacing them. All of them are now carried through, with an explicit `init` still winning per the fetch contract. Both are covered by tests driving a real redirect through a real proxy, and both were confirmed to fail without the fix. - `@modelcontextprotocol/ext-apps` was added only to the CLI's `external` list. AGENTS.md requires every root-declared runtime dependency in all three, since which client reaches it is a function of what `core/` imports rather than of the client's own code. Added to the web runner and TUI lists too. - The `BUNDLED_CLIENTS` test compared the hard-coded list against a hard-coded copy of itself, so a new tsup-bundled client would have left it green while going unchecked. It now discovers clients from disk (a client is tsup-bundled iff it ships a tsup config) and compares. Verified it fails when a stub client is added. - AGENTS.md's project tree still described `core/mcp/node/` as only the stdio transport factory; it now carries `proxyFetch.ts` and its role. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
dbe95e58ed
commit
96f96aad41
1 changed files with 135 additions and 17 deletions
|
|
@ -9,6 +9,14 @@
|
|||
|
||||
import { test, describe } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readdirSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
|
||||
import {
|
||||
BUNDLED_CLIENTS,
|
||||
|
|
@ -263,13 +271,38 @@ describe("evaluateClient", () => {
|
|||
});
|
||||
|
||||
describe("BUNDLED_CLIENTS", () => {
|
||||
test("covers every tsup-bundled client", () => {
|
||||
// `clients/launcher` is plain tsc and emits no bundle, so it is deliberately
|
||||
// absent. If a fourth bundled client appears, it must be enrolled here.
|
||||
assert.deepEqual(BUNDLED_CLIENTS.map((c) => c.name).sort(), [
|
||||
"cli",
|
||||
"tui",
|
||||
"web",
|
||||
]);
|
||||
test("is enrolled from what is actually on disk, not from itself", () => {
|
||||
// Asserting the hard-coded list equals a hard-coded copy of itself proves
|
||||
// nothing — adding `clients/foo` with a tsup build would leave it green
|
||||
// while the new bundle went unchecked. So the expected set is DISCOVERED:
|
||||
// a client is tsup-bundled iff it ships a tsup config. `clients/launcher`
|
||||
// is plain tsc, emits no bundle, and is correctly absent.
|
||||
const clientsDir = path.join(repoRoot, "clients");
|
||||
const discovered = readdirSync(clientsDir)
|
||||
.filter((name) =>
|
||||
readdirSync(path.join(clientsDir, name)).some((f) =>
|
||||
/^tsup(\..+)?\.config\.ts$/.test(f),
|
||||
),
|
||||
)
|
||||
.sort();
|
||||
|
||||
assert.ok(discovered.length > 0, "no tsup-bundled clients discovered");
|
||||
assert.deepEqual(
|
||||
BUNDLED_CLIENTS.map((c) => c.name).sort(),
|
||||
discovered,
|
||||
"BUNDLED_CLIENTS is out of sync with the clients that ship a tsup config",
|
||||
);
|
||||
});
|
||||
|
||||
test("points each client at a config and build dir that exist", () => {
|
||||
for (const client of BUNDLED_CLIENTS) {
|
||||
assert.ok(
|
||||
existsSync(path.join(repoRoot, client.config)),
|
||||
`${client.name}: missing ${client.config}`,
|
||||
);
|
||||
// The build dir is produced by `npm run build`; only assert the path is
|
||||
// under the client, since the guard itself reports a missing build.
|
||||
assert.match(client.build, new RegExp(`^clients/${client.name}/`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue