UNPKG

gqty

Version:

The No-GraphQL Client for TypeScript

326 lines (321 loc) • 11.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const debounceMicrotasks = require('debounce-microtasks'); const multidict = require('multidict'); const index = require('../Accessor/index.js'); const deferred = require('../Utils/deferred.js'); const pick = require('../Utils/pick.js'); const batching = require('./batching.js'); const context = require('./context.js'); const resolveSelections = require('./resolveSelections.js'); const subscriber = require('./subscriber.js'); const updateCaches = require('./updateCaches.js'); const pendingQueries = /* @__PURE__ */ new WeakMap(); const getIntersection = (subject, object) => { if (typeof subject.intersection === "function") { return subject.intersection(object); } const intersection = /* @__PURE__ */ new Set(); for (const item of object) { if (subject.has(item)) { intersection.add(item); } } return intersection; }; const createResolvers = ({ aliasLength, batchWindow, cache: resolverCache, debugger: debug, depthLimit, fetchOptions, fetchOptions: { cachePolicy: defaultCachePolicy = "default", retryPolicy: defaultRetryPoliy }, scalars, schema, parentContext }) => { const correlatedCaches = new multidict.MultiDict(); const subscriber$1 = subscriber.isWsClient(fetchOptions.subscriber) ? subscriber.createSubscriber(fetchOptions.subscriber) : fetchOptions.subscriber; const createResolver = ({ cachePolicy = defaultCachePolicy, extensions, onSelect, onSubscribe, operationName, retryPolicy = defaultRetryPoliy } = {}) => { const prevSelections = /* @__PURE__ */ new Set(); const replaceSet = (target, source) => { target.clear(); for (const value of source) { target.add(value); } }; const selections = /* @__PURE__ */ new Set(); const context$1 = context.createContext({ aliasLength, cache: resolverCache, cachePolicy, depthLimit, scalars, schema }); context$1.subscribeSelect((selection, selectionCache) => { const targetSelections = selectionCache === void 0 ? ( // For empty arrays and null objects, trigger sub-selections made // in previous selections. getIntersection(selection.getLeafNodes(), prevSelections) ) : [selection]; for (const selection2 of targetSelections) { if (!selections.has(selection2)) { if (false === (onSelect == null ? void 0 : onSelect(selection2, selectionCache))) { continue; } selections.add(selection2); parentContext == null ? void 0 : parentContext.select(selection2, selectionCache); } } }); const { accessor } = index.createSchemaAccessor(context$1); let activePromise; const resolve = async () => { if (selections.size === 0) { if (process.env.NODE_ENV !== "production") { console.warn( "[GQty] No selections found. If you are reading from the global accessors, try using the first argument instead." ); } return; } if (!context$1.shouldFetch) return; if (cachePolicy === "only-if-cached") { throw new TypeError("Failed to fetch"); } const selectionsCacheKey = `${operationName != null ? operationName : cachePolicy === "no-store" ? "no-store" : "default"}`; const pendingSelections = batching.addSelections( resolverCache, selectionsCacheKey, selections ); correlatedCaches.set(pendingSelections, context$1.cache); if (!pendingQueries.has(pendingSelections)) { pendingQueries.set( pendingSelections, // Batching happens at the end of microtask queue debounceMicrotasks.debounceMicrotaskPromise( async () => { var _a, _b; pendingQueries.delete(pendingSelections); if (batchWindow) { await new Promise( (resolve2) => setTimeout(resolve2, batchWindow) ); } const uniqueSelections = /* @__PURE__ */ new Set(); (_a = batching.getSelectionsSet(resolverCache, selectionsCacheKey)) == null ? void 0 : _a.forEach( (selections2) => { selections2.forEach((selection) => { uniqueSelections.add(selection); }); } ); batching.delSelectionSet(resolverCache, selectionsCacheKey); const results = await resolveSelections.fetchSelections(uniqueSelections, { cache: context$1.cache, debugger: debug, extensions, fetchOptions: { ...fetchOptions, cachePolicy, retryPolicy }, operationName }); const targetCaches = (_b = correlatedCaches.get(pendingSelections)) != null ? _b : /* @__PURE__ */ new Set(); if (cachePolicy !== "no-store") { targetCaches.add(resolverCache); } updateCaches.updateCaches(results, targetCaches, { skipNotify: promiseDropped() || !context$1.notifyCacheUpdate }); correlatedCaches.delete(resolverCache); return results; }, // When naughty users are adding selections every next microtask, we // forcibly start the fetch after a number of delays. This number is // picked arbitrarily, it should be a number that is large enough to // prevent excessive fetches but small enough to not block the // actual fetch indefinitely. { debounceLimit: 20, limitAction: "invoke" } ) ); const currentPromise = pendingQueries.get(pendingSelections)(); activePromise = currentPromise; const promiseDropped = () => activePromise !== void 0 && currentPromise !== activePromise; currentPromise.then( () => { if (promiseDropped()) return; if (selections.size === 0) return; replaceSet(prevSelections, selections); selections.clear(); }, () => { } ).finally(() => { if (promiseDropped()) return; context$1.reset(); activePromise = void 0; }); } return pendingQueries.get(pendingSelections)(); }; const subscribe = ({ onComplete, onError, onNext } = {}) => { if (selections.size === 0) { if (process.env.NODE_ENV !== "production") { console.warn( "[GQty] No selections found! If you are reading from the global accessors, try using the first argument instead." ); } return () => { }; } const unsubscibers = /* @__PURE__ */ new Set(); const unsubscribe = () => { for (const unsubscribe2 of unsubscibers) { unsubscribe2(); } }; if (onNext) { const unsubscribe2 = context$1.cache.subscribe( [...selections].map((s) => s.cacheKeys.join(".")), (data) => onNext({ data }) ); unsubscibers.add(unsubscribe2); } const subscriptionSelections = /* @__PURE__ */ new Set(); const promises = []; if (context$1.shouldFetch) { for (const selection of selections) { if (selection.root.key === "subscription") { selections.delete(selection); subscriptionSelections.add(selection); } } if (selections.size > 0) { promises.push(resolve().catch(onError)); } } for (const selection of subscriptionSelections) { selections.add(selection); } if (subscriptionSelections.size) { let lastSelectionsUpdated = false; const promise = new Promise((resolve2, reject) => { const unsubscribe2 = resolveSelections.subscribeSelections( subscriptionSelections, ({ data, error, extensions: extensions2 }) => { if (error) { onError == null ? void 0 : onError(error); reject(error); } else if (data !== void 0) { updateCaches.updateCaches( [{ data, error, extensions: extensions2 }], cachePolicy !== "no-store" && context$1.cache !== resolverCache ? [context$1.cache, resolverCache] : [context$1.cache] ); if (!lastSelectionsUpdated) { lastSelectionsUpdated = true; if (selections.size > 0) { replaceSet(prevSelections, selections); } } } else ; }, { cache: context$1.cache, debugger: debug, extensions, fetchOptions: { ...fetchOptions, cachePolicy, retryPolicy, subscriber: subscriber$1 }, operationName, onSubscribe: () => queueMicrotask(() => onSubscribe == null ? void 0 : onSubscribe(unsubscribe2)), onComplete: () => resolve2() } ); unsubscibers.add(unsubscribe2); }); promises.push(promise); } Promise.allSettled(promises).finally(onComplete); return unsubscribe; }; return { accessor, context: context$1, resolve, restorePreviousSelections: () => replaceSet(selections, prevSelections), selections, subscribe }; }; return { createResolver, resolve: async (fn, options) => { var _a, _b; const { accessor, resolve, selections } = createResolver(options); const dataFn = () => fn(accessor); dataFn(); const fetchPromise = resolve().then(dataFn, options == null ? void 0 : options.onError); if ((_a = options == null ? void 0 : options.awaitsFetch) != null ? _a : true) { await fetchPromise; } (_b = options == null ? void 0 : options.onFetch) == null ? void 0 : _b.call(options, fetchPromise); const result = dataFn(); if (result === void 0) { return pick.pick(accessor, selections); } return result; }, subscribe: (fn, { onSubscribe, ...options } = {}) => { const { accessor, selections, subscribe } = createResolver({ ...options, onSubscribe: (unsubscribe2) => { onSubscribe == null ? void 0 : onSubscribe(() => { unsubscribe2(); observable.complete(); }); } }); fn(accessor); const unsubscribe = subscribe({ onError: (error) => { if (observable.throw === void 0) { throw error; } observable.throw(error); }, onNext(value) { var _a; const message = (_a = fn(accessor)) != null ? _a : value; observable.send(message); } }); const observable = deferred.createDeferredIterator(); if (selections.size === 0) { observable.complete(); } return { ...observable, unsubscribe }; } }; }; exports.createResolvers = createResolvers;