UNPKG

gqty

Version:

The No-GraphQL Client for TypeScript

305 lines (300 loc) • 9.56 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const query = require('../Cache/query.js'); const index = require('../Error/index.js'); const useMetaStateHack = require('../Helpers/useMetaStateHack.js'); const QueryBuilder = require('../QueryBuilder.js'); const subscriber = require('./subscriber.js'); const retry = require('../Error/retry.js'); const fetchSelections = (selections, { cache, debugger: debug, extensions: customExtensions, fetchOptions, operationName }) => Promise.all( QueryBuilder.buildQuery(selections, operationName).map( async ({ query: query$1, variables, operationName: operationName2, extensions: { type, hash } = {} }) => { if (!type) throw new index.GQtyError(`Invalid query type: ${type}`); if (!hash) throw new index.GQtyError(`Expected query hash.`); const queryPayload = { query: query$1, variables, operationName: operationName2, extensions: { ...customExtensions, type, hash } }; const promise = query.dedupePromise( cache, hash, type === "subscription" ? () => doSubscribeOnce(queryPayload, fetchOptions) : () => doFetch(queryPayload, { ...fetchOptions, selections }) ).then(({ data, errors, extensions }) => { const result = { data, extensions: { ...extensions, ...queryPayload.extensions } }; if (errors == null ? void 0 : errors.length) { result.error = index.GQtyError.fromGraphQLErrors(errors); } return result; }); await (debug == null ? void 0 : debug.dispatch({ cache, request: queryPayload, promise, selections })); return await promise; } ) ); const subsRef = /* @__PURE__ */ new WeakMap(); const subscribeSelections = (selections, fn, { cache, debugger: debug, extensions: customExtensions, fetchOptions: { subscriber: subscriber$1 }, operationName, onSubscribe, onComplete }) => { const unsubscribers = /* @__PURE__ */ new Set(); Promise.all( QueryBuilder.buildQuery(selections, operationName).map( async ({ query: query$1, variables, operationName: operationName2, extensions: { type, hash } = {} }) => { if (!type) throw new index.GQtyError(`Invalid query type: ${type}`); if (!hash) throw new index.GQtyError(`Expected query hash.`); if (type === "subscription" && !subscriber$1) { throw new index.GQtyError(`Missing subscriber for subscriptions.`); } const queryPayload = { query: query$1, variables, operationName: operationName2, extensions: { ...customExtensions, type, hash } }; let subscriptionId; if (subscriber.isWsClient(subscriber$1)) { if (onSubscribe) { subscriber$1.onSubscribe(onSubscribe); } if (debug) { const unsub = subscriber$1.on("message", (message) => { var _a; switch (message.type) { case "connection_ack": { break; } case "subscribe": { if (((_a = message.payload.extensions) == null ? void 0 : _a.hash) !== hash) return; subscriptionId = message.id; debug.dispatch({ cache, label: `[id=${subscriptionId}] [create]`, request: queryPayload, selections }); unsub(); break; } } }); } } else { subscriptionId = "EventSource"; onSubscribe == null ? void 0 : onSubscribe(); } const next = ({ data, errors, extensions }) => { if (data === void 0) return; const result = { data, extensions: { ...extensions, ...queryPayload.extensions } }; if (errors == null ? void 0 : errors.length) { result.error = index.GQtyError.fromGraphQLErrors(errors); } debug == null ? void 0 : debug.dispatch({ cache, label: subscriptionId ? `[id=${subscriptionId}] [data]` : void 0, request: queryPayload, promise: result.error ? Promise.reject(result) : Promise.resolve(result), selections }); fn(result); }; const error = (error2) => { if (error2 == null) { throw new index.GQtyError(`Subscription sink closed without an error.`); } debug == null ? void 0 : debug.dispatch({ cache, label: subscriptionId ? `[id=${subscriptionId}] [error]` : void 0, request: queryPayload, selections }); fn({ error: error2, extensions: queryPayload.extensions }); }; let dispose; const promise = query.dedupePromise(cache, hash, () => { return new Promise((complete) => { dispose = subscriber$1 == null ? void 0 : subscriber$1.subscribe( queryPayload, { next, error(e) { if (Array.isArray(e)) { error(index.GQtyError.fromGraphQLErrors(e)); } else if (!isCloseEvent(e)) { if (e instanceof Error) { error(index.GQtyError.create(e)); } else { console.error("Unknown subscription error:", e); } } this.complete(); }, complete() { debug == null ? void 0 : debug.dispatch({ cache, label: subscriptionId ? `[id=${subscriptionId}] [unsubscribe]` : void 0, request: queryPayload, selections }); complete(); } } ); }); }); if (dispose) { subsRef.set(promise, { dispose, count: 1 }); } else if (subsRef.get(promise)) { subsRef.get(promise).count++; } unsubscribers.add(() => { const ref = subsRef.get(promise); if (ref && --ref.count <= 0) { setTimeout(() => { if (ref.count <= 0) { ref.dispose(); } }); } }); return promise; } ) ).finally(() => onComplete == null ? void 0 : onComplete()); return () => { unsubscribers.forEach((fn2) => fn2()); }; }; const doFetch = async (payload, { fetcher, retryPolicy, selections, ...fetchOptions }) => { const doDoFetch = () => fetcher(payload, fetchOptions); try { const promise = doDoFetch(); useMetaStateHack.notifyFetch(promise, selections); return await promise; } catch (error) { if ( // User doesn't want reties !retryPolicy || // Let everything unknown through !(error instanceof Error) || // GQtyErrors are supposed to be terminating error instanceof index.GQtyError ) { throw error; } return new Promise((resolve, reject) => { retry.doRetry(retryPolicy, { onLastTry: async () => { try { const promise = doDoFetch(); useMetaStateHack.notifyRetry(promise, selections); resolve(await promise); } catch (e) { reject(e); } }, onRetry: async () => { const promise = doDoFetch(); useMetaStateHack.notifyRetry(promise, selections); resolve(await promise); } }); }); } }; const doSubscribeOnce = async ({ query, variables, operationName }, { subscriber }) => { if (!subscriber) { throw new index.GQtyError(`Subscription client is required for subscritions.`); } return new Promise( (resolve, reject) => { let result; const unsubscribe = subscriber.subscribe( { query, variables: variables != null ? variables : {}, operationName: operationName != null ? operationName : void 0 }, { next(data) { result = data; unsubscribe(); }, error(error) { if (isCloseEvent(error)) { resolve(result); } else if (Array.isArray(error)) { reject(index.GQtyError.fromGraphQLErrors(error)); } else { reject(error); } }, complete() { if (!result) { throw new index.GQtyError(`Subscription completed without data`); } resolve(result); } } ); } ); }; const isCloseEvent = (input) => { var _a, _b; const error = input; if (!error || typeof error !== "object") return false; return error.type === "close" && ((_b = (_a = error.target) == null ? void 0 : _a.constructor) == null ? void 0 : _b.name) === "WebSocket" || typeof error.code === "number" && [ 4004, 4005, 4400, 4401, 4403, 4406, 4408, 4409, 4429, 4499, 4500, 4504 ].includes(error.code); }; exports.fetchSelections = fetchSelections; exports.isCloseEvent = isCloseEvent; exports.subscribeSelections = subscribeSelections;