/*** IMPORT ------------------------------------------- ***/ import { createClient, type Client, type ClientOptions } from "graphql-sse"; /*** UTILITY ------------------------------------------ ***/ import type { Fetcher, FetcherOptions, FetcherResult } from "./types.ts"; /*** EXPORT ------------------------------------------- ***/ export type SseFetcherOptions = FetcherOptions & { client?: Partial>; }; export function createSseFetcher(options: SseFetcherOptions): Fetcher { const client: Client = createClient({ fetchFn: options.fetch, headers: options.headers, url: options.url, ...options.client }); return (req) => { return { [Symbol.asyncIterator](): AsyncIterator { const queue: FetcherResult[] = []; const resolvers: ((step: IteratorResult) => void)[] = []; let done = false; let error: unknown = null; const dispose = client.subscribe>( { extensions: req.headers as Record | undefined, operationName: req.operationName ?? undefined, query: req.query, variables: req.variables }, { complete() { done = true; flush(); }, error(err) { error = err; done = true; flush(); }, next(value) { queue.push(value as FetcherResult); flush(); } } ); function flush() { while (resolvers.length > 0 && (queue.length > 0 || done)) { const resolve = resolvers.shift(); if (!resolve) break; if (queue.length > 0) resolve({ done: false, value: queue.shift() as FetcherResult }); else if (error) resolve({ done: true, value: undefined }); else resolve({ done: true, value: undefined }); } } return { next(): Promise> { if (error) { const err = error; error = null; return Promise.reject(err); } if (queue.length > 0) return Promise.resolve({ done: false, value: queue.shift() as FetcherResult }); if (done) return Promise.resolve({ done: true, value: undefined }); return new Promise>((resolve) => { resolvers.push(resolve); }); }, return(): Promise> { dispose(); done = true; flush(); return Promise.resolve({ done: true, value: undefined }); } }; } }; }; }