ohmygql
Version:
[![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![Github Actions][github-actions-src]][github-actions-href] [![Codecov][codecov-src]][codecov-href]
147 lines (142 loc) • 4.51 kB
JavaScript
import { print } from 'graphql';
import { $fetch, FetchError } from 'ofetch';
import { extractOperation } from './utils.mjs';
import { createClient } from 'graphql-ws';
class GqlError extends Error {
constructor(message, args) {
super(message);
this.name = "GqlError";
this.status = args.status;
this.operation = args.operation;
this.gqlErrors = args.gqlErrors;
}
}
function wsClient(options) {
let restartRequested = false;
let restart = () => {
restartRequested = true;
};
const client = createClient({
...options,
on: {
...options.on,
opened: (socket) => {
options.on?.opened?.(socket);
restart = () => {
if (socket.readyState === WebSocket.OPEN) {
socket.close(4205, "Client Restart");
} else {
restartRequested = true;
}
};
if (restartRequested) {
restartRequested = false;
restart();
}
}
}
});
return { ...client, restart: () => restart() };
}
const GqlClient = (input) => {
const opts = typeof input === "string" ? { host: input } : input;
let fetchOptions = {};
const setHost = (host) => {
if (!host) {
return;
}
opts.host = host;
};
const setOptions = (opts2) => {
if (!opts2) {
fetchOptions = {};
return;
}
for (const [k, v] of Object.entries(opts2)) {
if (!v) {
delete fetchOptions[k];
continue;
}
fetchOptions[k] = typeof v !== "object" ? v : { ...fetchOptions[k], ...v };
}
};
const setHeaders = (headers) => setOptions({ headers });
const setMiddleware = (mw) => {
if (!mw) {
return;
}
opts.middleware = opts.middleware || {};
for (const [k, v] of Object.entries(mw)) {
opts.middleware[k] = v;
}
};
async function execute(...args) {
let query = args?.[0]?.query || args?.[0];
query = typeof query === "string" ? query : print(query);
const variables = args?.[0]?.variables || args?.[1];
fetchOptions.headers = fetchOptions?.headers || {};
fetchOptions.headers = {
...opts?.headers,
...args?.[0]?.headers || args?.[2],
...fetchOptions?.headers
};
const useGET = opts?.useGETForQueries && extractOperation(query)?.type === "query";
const reqOptions = {
query,
...variables && { variables: !useGET ? variables : JSON.stringify(variables) }
};
const res = await $fetch.raw(opts.host, {
...!useGET && { method: "POST" },
...fetchOptions,
headers: {
"Content-Type": "application/json",
...fetchOptions?.headers
},
...opts?.middleware,
...!useGET ? { body: reqOptions } : { params: reqOptions }
}).catch((e) => e);
if (res instanceof FetchError || res?._data?.errors) {
const gqlErrors = (res instanceof FetchError ? res.data?.errors : res._data?.errors) || void 0;
const response = res instanceof FetchError ? res.response : res;
const status = response?.status;
const message = gqlErrors?.map((e) => e.message).join(", ") || response && `${status} ${response?.statusText}` || "Fetch failed";
throw new GqlError(message, { status, gqlErrors, operation: extractOperation(query) });
}
return res._data?.data;
}
function subscribe(...args) {
let query = args?.[0]?.query || args?.[0];
query = typeof query === "string" ? query : print(query);
const variables = args?.[0]?.variables || args?.[1];
const options = args?.[0]?.options || args?.[2];
const url = opts?.wsHost || opts?.host.replace(/^http/, "ws").replace(/^https/, "wss");
if (!url || !url.startsWith("ws")) {
throw new Error("Invalid websocket host");
}
const client = wsClient({ url, ...opts?.wsOptions, ...options });
let unsubscribe = () => {
};
let onError;
let onResult;
let onComplete;
return {
restart: () => client.restart(),
unsubscribe: () => unsubscribe(),
onError: (cb) => onError = cb,
onResult: (cb) => onResult = cb,
onComplete: (cb) => onComplete = cb,
subscribe: () => {
unsubscribe = client.subscribe({ query, variables }, {
next: onResult || (() => {
}),
error: onError || (() => {
}),
complete: onComplete || (() => {
})
});
}
};
}
return { execute, subscribe, setHost, setOptions, setHeaders, setMiddleware };
};
export { GqlClient, GqlError };