// using tauri command to send request // see src-tauri/src/stream.rs, and src-tauri/src/main.rs // 1. invoke('stream_fetch', {url, method, headers, body}), get response with headers. // 2. listen event: `stream-response` multi times to get body type ResponseEvent = { id: number; payload: { request_id: number; status?: number; chunk?: number[]; }; }; type StreamResponse = { request_id: number; status: number; status_text: string; headers: Record; }; export function fetch(url: string, options?: RequestInit): Promise { if (window.__TAURI__) { const { signal, method = "GET", headers: _headers = {}, body = [], } = options || {}; let unlisten: Function | undefined; let setRequestId: Function | undefined; const requestIdPromise = new Promise((resolve) => (setRequestId = resolve)); const ts = new TransformStream(); const writer = ts.writable.getWriter(); let closed = false; const close = () => { if (closed) return; closed = true; unlisten && unlisten(); writer.ready.then(() => { writer.close().catch((e) => console.error(e)); }); }; if (signal) { signal.addEventListener("abort", () => close()); } // @ts-ignore 2. listen response multi times, and write to Response.body window.__TAURI__.event .listen("stream-response", (e: ResponseEvent) => requestIdPromise.then((request_id) => { const { request_id: rid, chunk, status } = e?.payload || {}; if (request_id != rid) { return; } if (chunk) { writer.ready.then(() => { writer.write(new Uint8Array(chunk)); }); } else if (status === 0) { // end of body close(); } }), ) .then((u: Function) => (unlisten = u)); const headers: Record = { Accept: "application/json, text/plain, */*", "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", "User-Agent": navigator.userAgent, }; for (const item of new Headers(_headers || {})) { headers[item[0]] = item[1]; } return window.__TAURI__ .invoke("stream_fetch", { method: method.toUpperCase(), url, headers, // TODO FormData body: typeof body === "string" ? Array.from(new TextEncoder().encode(body)) : [], }) .then((res: StreamResponse) => { const { request_id, status, status_text: statusText, headers } = res; setRequestId?.(request_id); const response = new Response(ts.readable, { status, statusText, headers, }); if (status >= 300) { setTimeout(close, 100); } return response; }) .catch((e) => { console.error("stream error", e); throw e; }); } return window.fetch(url, options); }