import type { LineMatch, TextQuery } from "../lib/text-search"; /** Session details the embedding page hands to the workbench (and on to this extension) via a command. */ export type EditorSessionInfo = { token: string; forgeOrigin: string; owner: string; repo: string; ref: string; refKind: "branch" | "tag" | "commit"; sha: string; writable: boolean; protected: boolean; user: string; containers: boolean; /** Set by "Open in container" on the forge: start a container and open a terminal after activation. */ startContainer?: boolean; /** File to open on start, e.g. from `/edit/?path=` or pressing `.` on a file page. */ openPath?: string | null; }; export type TreeEntry = { path: string; type: string; sha: string }; export type SearchRequest = TextQuery & { maxResults: number; /** Paths searched locally from the live room (or deleted there): the head copies would be stale. */ skip: string[]; excludeDirs: string[]; }; export type SearchResponse = { files: Array<{ path: string; matches: LineMatch[] }>; limitHit: boolean }; /** Token-only calls to the forge's editor API. */ export class ForgeApi { constructor(private session: EditorSessionInfo) {} update(session: EditorSessionInfo): void { this.session = session; } get token(): string { return this.session.token; } roomUrl(): string { const url = new URL("/api/editor/room", this.session.forgeOrigin); url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; return url.toString(); } async request(path: string, init: { method?: string; body?: unknown; signal?: AbortSignal } = {}): Promise { const response = await fetch(new URL(`/api/editor/${path}`, this.session.forgeOrigin), { method: init.method ?? "GET", headers: { authorization: `Bearer ${this.session.token}`, "content-type": "application/json" }, body: init.body === undefined ? undefined : JSON.stringify(init.body), credentials: "omit", signal: init.signal, }); const body = (await response.json().catch(() => ({}))) as T & { error?: string }; if (!response.ok) throw new Error(body.error ?? `Request failed (${response.status})`); return body; } tree(): Promise<{ sha: string; truncated: boolean; entries: TreeEntry[] }> { return this.request("tree"); } search(body: SearchRequest, signal: AbortSignal): Promise { return this.request("search", { method: "POST", body, signal }); } blob(path: string): Promise<{ kind: string; content: string | null }> { return this.request(`blob?path=${encodeURIComponent(path)}`); } }