@graphql-yoga/urql-exchange
Version:
87 lines (86 loc) • 3.77 kB
JavaScript
import { pipe, share, filter, takeUntil, mergeMap, merge, make, } from 'wonka';
import { makeResult, makeErrorResult, mergeResultPatch, getOperationName, } from '@urql/core';
import { isAsyncIterable } from '@graphql-tools/utils';
import { SubscriptionProtocol, UrlLoader, } from '@graphql-tools/url-loader';
export function yogaExchange(options) {
const urlLoader = new UrlLoader();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function makeYogaSource(operation) {
const operationName = getOperationName(operation.query);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const executionRequest = {
document: operation.query,
operationName,
operationType: operation.kind,
variables: operation.variables,
context: operation.context,
extensions: {
endpoint: operation.context.url,
headers: operation.context.headers,
},
};
const extraFetchOptions = typeof operation.context.fetchOptions === 'function'
? operation.context.fetchOptions()
: operation.context.fetchOptions;
const executor = urlLoader.getExecutorAsync(options?.endpoint || operation.context.url, {
subscriptionsProtocol: SubscriptionProtocol.SSE,
multipart: true,
customFetch: operation.context.fetch,
useGETForQueries: !!operation.context.preferGetMethod,
headers: extraFetchOptions?.headers,
method: extraFetchOptions?.method,
credentials: extraFetchOptions?.credentials,
...options,
});
return make((observer) => {
let ended = false;
executor(executionRequest)
.then(async (result) => {
if (ended || !result) {
return;
}
if (!isAsyncIterable(result)) {
observer.next(makeResult(operation, result));
}
else {
let prevResult = null;
for await (const value of result) {
if (value) {
prevResult = prevResult
? mergeResultPatch(prevResult, value)
: makeResult(operation, value);
observer.next(prevResult);
}
if (ended) {
break;
}
}
}
observer.complete();
})
.catch((error) => {
observer.next(makeErrorResult(operation, error));
})
.finally(() => {
ended = true;
observer.complete();
});
return () => {
ended = true;
};
});
}
return function yogaExchangeFn({ forward }) {
return function yogaExchangeIO(ops$) {
const sharedOps$ = share(ops$);
const executedOps$ = pipe(sharedOps$, filter((operation) => operation.kind === 'query' ||
operation.kind === 'mutation' ||
operation.kind === 'subscription'), mergeMap((operation) => {
const teardown$ = pipe(sharedOps$, filter((op) => op.kind === 'teardown' && op.key === operation.key));
return pipe(makeYogaSource(operation), takeUntil(teardown$));
}));
const forwardedOps$ = pipe(sharedOps$, filter((operation) => operation.kind === 'teardown'), forward);
return merge([executedOps$, forwardedOps$]);
};
};
}