stream.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // using tauri command to send request
  2. // see src-tauri/src/stream.rs, and src-tauri/src/main.rs
  3. // 1. invoke('stream_fetch', {url, method, headers, body}), get response with headers.
  4. // 2. listen event: `stream-response` multi times to get body
  5. type ResponseEvent = {
  6. id: number;
  7. payload: {
  8. request_id: number;
  9. status?: number;
  10. chunk?: number[];
  11. };
  12. };
  13. type StreamResponse = {
  14. request_id: number;
  15. status: number;
  16. status_text: string;
  17. headers: Record<string, string>;
  18. };
  19. export function fetch(url: string, options?: RequestInit): Promise<any> {
  20. if (window.__TAURI__) {
  21. const { signal, method = "GET", headers = {}, body = [] } = options || {};
  22. let unlisten: Function | undefined;
  23. let request_id = 0;
  24. const ts = new TransformStream();
  25. const writer = ts.writable.getWriter();
  26. const close = () => {
  27. unlisten && unlisten();
  28. writer.ready.then(() => {
  29. try {
  30. writer.releaseLock();
  31. } catch (e) {
  32. console.error(e);
  33. }
  34. ts.writable.close();
  35. });
  36. };
  37. if (signal) {
  38. signal.addEventListener("abort", () => close());
  39. }
  40. // @ts-ignore 2. listen response multi times, and write to Response.body
  41. window.__TAURI__.event
  42. .listen("stream-response", (e: ResponseEvent) => {
  43. const { request_id: rid, chunk, status } = e?.payload || {};
  44. if (request_id != rid) {
  45. return;
  46. }
  47. if (chunk) {
  48. writer &&
  49. writer.ready.then(() => {
  50. writer && writer.write(new Uint8Array(chunk));
  51. });
  52. } else if (status === 0) {
  53. // end of body
  54. close();
  55. }
  56. })
  57. .then((u: Function) => (unlisten = u));
  58. return window.__TAURI__
  59. .invoke("stream_fetch", {
  60. method,
  61. url,
  62. headers,
  63. // TODO FormData
  64. body:
  65. typeof body === "string"
  66. ? Array.from(new TextEncoder().encode(body))
  67. : [],
  68. })
  69. .then((res: StreamResponse) => {
  70. request_id = res.request_id;
  71. const { status, status_text: statusText, headers } = res;
  72. return new Response(ts.readable, { status, statusText, headers });
  73. })
  74. .catch((e) => {
  75. console.error("stream error", e);
  76. throw e;
  77. });
  78. }
  79. return window.fetch(url, options);
  80. }