mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
Run oxfmt once over the whole repository (formatting only, no logic changes) and add `pnpm format:check` to the frontend CI job so unformatted code no longer builds up. `.oxfmtrc.json` now skips the generated theme docs, test fixtures and code blocks inside Markdown. Co-authored-by: jaw52 <[email protected]>
179 lines
5.5 KiB
TypeScript
179 lines
5.5 KiB
TypeScript
/** HTTP Client state: selected environment per file, responses, history, running request. */
|
|
import { create } from "zustand";
|
|
import {
|
|
call,
|
|
commands,
|
|
isCancelled,
|
|
nextRequestId,
|
|
type HttpHistoryEntry,
|
|
type HttpExchange,
|
|
type HttpRequestBlock,
|
|
type RequestId,
|
|
} from "@/platform/ipc";
|
|
import { notifications, showServiceView } from "@/workbench/contributions";
|
|
|
|
export const SERVICE_VIEW = "httpClient";
|
|
const ENV_KEY = "openidea.httpClient.environments";
|
|
|
|
function readEnvChoices(): Record<string, string> {
|
|
try {
|
|
const raw = localStorage.getItem(ENV_KEY);
|
|
return raw ? (JSON.parse(raw) as Record<string, string>) : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
interface Running {
|
|
path: string | null;
|
|
index: number;
|
|
request: RequestId;
|
|
}
|
|
|
|
interface HttpClientState {
|
|
/** Selected environment per request file (absent: none). */
|
|
envByFile: Record<string, string>;
|
|
/** Environments available per request file. */
|
|
environments: Record<string, string[]>;
|
|
/** History entries, newest first (this session's runs included). */
|
|
history: HttpHistoryEntry[];
|
|
/** Loaded exchanges by entry id. */
|
|
exchanges: Record<string, HttpExchange>;
|
|
selected: string | null;
|
|
running: Running | null;
|
|
}
|
|
|
|
export const useHttpClient = create<HttpClientState>(() => ({
|
|
envByFile: readEnvChoices(),
|
|
environments: {},
|
|
history: [],
|
|
exchanges: {},
|
|
selected: null,
|
|
running: null,
|
|
}));
|
|
|
|
export function setEnvironment(path: string, env: string | null) {
|
|
useHttpClient.setState((s) => {
|
|
const envByFile = { ...s.envByFile };
|
|
if (env) envByFile[path] = env;
|
|
else delete envByFile[path];
|
|
try {
|
|
localStorage.setItem(ENV_KEY, JSON.stringify(envByFile));
|
|
} catch {
|
|
// Per-viewer convenience only.
|
|
}
|
|
return { envByFile };
|
|
});
|
|
}
|
|
|
|
export async function loadEnvironments(path: string): Promise<string[]> {
|
|
try {
|
|
const names = await call(commands.httpEnvironments(path));
|
|
useHttpClient.setState((s) => ({ environments: { ...s.environments, [path]: names } }));
|
|
return names;
|
|
} catch (e) {
|
|
notifications().notify({
|
|
level: "warning",
|
|
title: "HTTP Client environments",
|
|
body: e instanceof Error ? e.message : String(e),
|
|
key: "http-env",
|
|
});
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** The environment a run of `path` uses: the chosen one if it still exists. */
|
|
export function effectiveEnvironment(path: string | null): string | null {
|
|
if (!path) return null;
|
|
const s = useHttpClient.getState();
|
|
const chosen = s.envByFile[path];
|
|
const available = s.environments[path];
|
|
if (chosen && (!available || available.includes(chosen))) return chosen;
|
|
return null;
|
|
}
|
|
|
|
export async function refreshHistory() {
|
|
try {
|
|
const history = await call(commands.httpHistory());
|
|
useHttpClient.setState({ history });
|
|
} catch {
|
|
// No project.
|
|
}
|
|
}
|
|
|
|
export async function selectEntry(id: string) {
|
|
useHttpClient.setState({ selected: id });
|
|
if (useHttpClient.getState().exchanges[id]) return;
|
|
try {
|
|
const ex = await call(commands.httpHistoryGet(id));
|
|
useHttpClient.setState((s) => ({ exchanges: { ...s.exchanges, [id]: ex } }));
|
|
} catch (e) {
|
|
notifications().notify({
|
|
level: "error",
|
|
title: "Cannot open the response",
|
|
body: e instanceof Error ? e.message : String(e),
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function clearHistory() {
|
|
await call(commands.httpHistoryClear());
|
|
useHttpClient.setState({ history: [], exchanges: {}, selected: null });
|
|
}
|
|
|
|
export function parseRequests(text: string): Promise<HttpRequestBlock[]> {
|
|
return commands.httpRequests(text);
|
|
}
|
|
|
|
/**
|
|
* Sends request `index` of `text` (the buffer of `path`). The response opens
|
|
* in the HTTP Client tool window. Returns false when cancelled or failed early.
|
|
*/
|
|
export async function runRequest(path: string | null, text: string, index: number): Promise<boolean> {
|
|
const busy = useHttpClient.getState().running;
|
|
if (busy) void commands.cancelRequest(busy.request);
|
|
const request = nextRequestId();
|
|
useHttpClient.setState({ running: { path, index, request } });
|
|
try {
|
|
if (path && !useHttpClient.getState().environments[path]) await loadEnvironments(path);
|
|
const ex = await call(commands.httpExecute(request, path, text, index, effectiveEnvironment(path)));
|
|
useHttpClient.setState((s) => ({
|
|
history: [ex.entry, ...s.history.filter((h) => h.id !== ex.entry.id)],
|
|
exchanges: { ...s.exchanges, [ex.entry.id]: ex },
|
|
selected: ex.entry.id,
|
|
}));
|
|
showServiceView(SERVICE_VIEW, false);
|
|
return true;
|
|
} catch (e) {
|
|
if (!isCancelled(e))
|
|
notifications().notify({
|
|
level: "error",
|
|
title: "Cannot send the request",
|
|
body: e instanceof Error ? e.message : String(e),
|
|
key: "http-run",
|
|
});
|
|
return false;
|
|
} finally {
|
|
if (useHttpClient.getState().running?.request === request) useHttpClient.setState({ running: null });
|
|
}
|
|
}
|
|
|
|
/** Runs every request of the file in order; stops when one is cancelled. */
|
|
export async function runAll(path: string | null, text: string): Promise<void> {
|
|
const blocks = await parseRequests(text);
|
|
for (const b of blocks) {
|
|
if (!(await runRequest(path, text, b.index))) return;
|
|
}
|
|
}
|
|
|
|
export function cancelRunning() {
|
|
const r = useHttpClient.getState().running;
|
|
if (r) void commands.cancelRequest(r.request);
|
|
}
|
|
|
|
/** The window switched projects: history, responses and environments belong to the previous one. */
|
|
export function resetHttpClient() {
|
|
cancelRunning();
|
|
useHttpClient.setState({ environments: {}, history: [], exchanges: {}, selected: null, running: null });
|
|
}
|