client.current and createScopedClient resolve transport via dispatchFor: web -> absolute origin + /api base with an X-Honcho-Upstream header; Tauri -> absolute instance URL + reqwest. Absolute base (not bare "/api") so openapi-fetch can construct a Request under node/undici and in the browser alike. Fleet fan-out is unchanged; fleet.test.tsx now asserts the proxy contract.
48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import { httpFetch } from "@/lib/http";
|
|
import { isTauri } from "@/lib/platform";
|
|
|
|
/** Same-origin path prefix the web build issues all Honcho calls through. */
|
|
export const API_PREFIX = "/api";
|
|
/** Request header naming the real Honcho upstream for the proxy to forward to. */
|
|
export const UPSTREAM_HEADER = "X-Honcho-Upstream";
|
|
/** Response header the proxy sets on its OWN refusals (so they aren't read as upstream auth). */
|
|
export const PROXY_REJECT_HEADER = "X-Honcho-Proxy-Reject";
|
|
|
|
export interface Dispatch {
|
|
baseUrl: string;
|
|
headers: Record<string, string>;
|
|
fetch: typeof globalThis.fetch;
|
|
}
|
|
|
|
function normalizeUpstream(url: string): string {
|
|
return url.trim().replace(/\/+$/, "");
|
|
}
|
|
|
|
/**
|
|
* Absolute same-origin base for the web proxy. Absolute (origin + `/api`), not the
|
|
* bare relative `/api`, so openapi-fetch and node/undici can construct a Request
|
|
* without an ambient document base — and so it resolves identically in the browser,
|
|
* behind a tunnel, and under jsdom.
|
|
*/
|
|
function webApiBase(): string {
|
|
const origin = typeof location !== "undefined" ? location.origin : "";
|
|
return `${origin}${API_PREFIX}`;
|
|
}
|
|
|
|
/**
|
|
* Resolve how to issue a request for an instance.
|
|
* - Web: same-origin `/api` + `X-Honcho-Upstream` header (proxy forwards server-side, no CORS).
|
|
* - Tauri: the absolute instance URL via reqwest (no browser same-origin policy).
|
|
*/
|
|
export function dispatchFor(instance: { baseUrl: string; token?: string }): Dispatch {
|
|
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
if (instance.token) headers.Authorization = `Bearer ${instance.token}`;
|
|
|
|
if (isTauri()) {
|
|
return { baseUrl: instance.baseUrl, headers, fetch: httpFetch };
|
|
}
|
|
|
|
headers[UPSTREAM_HEADER] = normalizeUpstream(instance.baseUrl);
|
|
return { baseUrl: webApiBase(), headers, fetch: httpFetch };
|
|
}
|