Spaces:
Sleeping
Sleeping
// 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<string, string>; | |
}; | |
export function fetch(url: string, options?: RequestInit): Promise<Response> { | |
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<string, string> = { | |
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 new Response("", { status: 599 }); | |
}); | |
} | |
return window.fetch(url, options); | |
} | |