stream.ts 2.7 KB

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