fetch-remote-text-by-line.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import type { BunFile } from 'bun';
  2. import { fetchWithRetry } from './fetch-retry';
  3. const decoder = new TextDecoder('utf-8');
  4. export async function* readFileByLine(file: string | BunFile): AsyncGenerator<string> {
  5. if (typeof file === 'string') {
  6. file = Bun.file(file);
  7. }
  8. let buf = '';
  9. for await (const chunk of file.stream()) {
  10. const chunkStr = decoder.decode(chunk).replaceAll('\r\n', '\n');
  11. for (let i = 0, len = chunkStr.length; i < len; i++) {
  12. const char = chunkStr[i];
  13. if (char === '\n') {
  14. yield buf;
  15. buf = '';
  16. } else {
  17. buf += char;
  18. }
  19. }
  20. }
  21. if (buf) {
  22. yield buf;
  23. }
  24. }
  25. export async function* createReadlineInterfaceFromResponse(resp: Response): AsyncGenerator<string> {
  26. if (!resp.body) {
  27. throw new Error('Failed to fetch remote text');
  28. }
  29. if (resp.bodyUsed) {
  30. throw new Error('Body has already been consumed.');
  31. }
  32. let buf = '';
  33. for await (const chunk of resp.body) {
  34. const chunkStr = decoder.decode(chunk).replaceAll('\r\n', '\n');
  35. for (let i = 0, len = chunkStr.length; i < len; i++) {
  36. const char = chunkStr[i];
  37. if (char === '\n') {
  38. yield buf;
  39. buf = '';
  40. } else {
  41. buf += char;
  42. }
  43. }
  44. }
  45. if (buf) {
  46. yield buf;
  47. }
  48. }
  49. export function fetchRemoteTextAndCreateReadlineInterface(url: string | URL, opt?: RequestInit): Promise<AsyncGenerator<string>> {
  50. return fetchWithRetry(url, opt).then(res => createReadlineInterfaceFromResponse(res));
  51. }