@urql/exchange-graphcache
Version:
A normalized and configurable cache exchange for urql
330 lines (322 loc) • 11.6 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var core = require('@urql/core');
/** Input parameters for the {@link relayPagination} factory. */
var defaultPageInfo = {
__typename: 'PageInfo',
endCursor: null,
startCursor: null,
hasNextPage: false,
hasPreviousPage: false
};
var ensureKey = x => typeof x === 'string' ? x : null;
var concatEdges = (cache, leftEdges, rightEdges) => {
var ids = new Set();
for (var i = 0, l = leftEdges.length; i < l; i++) {
var edge = leftEdges[i];
var node = cache.resolve(edge, 'node');
if (typeof node === 'string') ids.add(node);
}
var newEdges = leftEdges.slice();
for (var _i = 0, _l = rightEdges.length; _i < _l; _i++) {
var _edge = rightEdges[_i];
var _node = cache.resolve(_edge, 'node');
if (typeof _node === 'string' && !ids.has(_node)) {
ids.add(_node);
newEdges.push(_edge);
}
}
return newEdges;
};
var concatNodes = (leftNodes, rightNodes) => {
var ids = new Set();
for (var i = 0, l = leftNodes.length; i < l; i++) {
var node = leftNodes[i];
if (typeof node === 'string') ids.add(node);
}
var newNodes = leftNodes.slice();
for (var _i2 = 0, _l2 = rightNodes.length; _i2 < _l2; _i2++) {
var _node2 = rightNodes[_i2];
if (typeof _node2 === 'string' && !ids.has(_node2)) {
ids.add(_node2);
newNodes.push(_node2);
}
}
return newNodes;
};
var compareArgs = (fieldArgs, connectionArgs) => {
for (var key in connectionArgs) {
if (key === 'first' || key === 'last' || key === 'after' || key === 'before') {
continue;
} else if (!(key in fieldArgs)) {
return false;
}
var argA = fieldArgs[key];
var argB = connectionArgs[key];
if (typeof argA !== typeof argB || typeof argA !== 'object' ? argA !== argB : core.stringifyVariables(argA) !== core.stringifyVariables(argB)) {
return false;
}
}
for (var _key in fieldArgs) {
if (_key === 'first' || _key === 'last' || _key === 'after' || _key === 'before') {
continue;
}
if (!(_key in connectionArgs)) return false;
}
return true;
};
var getPage = (cache, entityKey, fieldKey) => {
var link = ensureKey(cache.resolve(entityKey, fieldKey));
if (!link) return null;
var typename = cache.resolve(link, '__typename');
var edges = cache.resolve(link, 'edges') || [];
var nodes = cache.resolve(link, 'nodes') || [];
if (typeof typename !== 'string') {
return null;
}
var page = {
__typename: typename,
edges,
nodes,
pageInfo: defaultPageInfo
};
var pageInfoKey = cache.resolve(link, 'pageInfo');
if (typeof pageInfoKey === 'string') {
var pageInfoType = ensureKey(cache.resolve(pageInfoKey, '__typename'));
var endCursor = ensureKey(cache.resolve(pageInfoKey, 'endCursor'));
var startCursor = ensureKey(cache.resolve(pageInfoKey, 'startCursor'));
var hasNextPage = cache.resolve(pageInfoKey, 'hasNextPage');
var hasPreviousPage = cache.resolve(pageInfoKey, 'hasPreviousPage');
var pageInfo = page.pageInfo = {
__typename: typeof pageInfoType === 'string' ? pageInfoType : 'PageInfo',
hasNextPage: typeof hasNextPage === 'boolean' ? hasNextPage : !!endCursor,
hasPreviousPage: typeof hasPreviousPage === 'boolean' ? hasPreviousPage : !!startCursor,
endCursor,
startCursor
};
if (pageInfo.endCursor === null) {
var edge = edges[edges.length - 1];
if (edge) {
var _endCursor = cache.resolve(edge, 'cursor');
pageInfo.endCursor = ensureKey(_endCursor);
}
}
if (pageInfo.startCursor === null) {
var _edge2 = edges[0];
if (_edge2) {
var _startCursor = cache.resolve(_edge2, 'cursor');
pageInfo.startCursor = ensureKey(_startCursor);
}
}
}
return page;
};
/** Creates a {@link Resolver} that combines pages that comply to the Relay pagination spec.
*
* @param params - A {@link PaginationParams} configuration object.
* @returns the created Relay pagination {@link Resolver}.
*
* @remarks
* `relayPagination` is a factory that creates a {@link Resolver} that can combine
* multiple pages on a field that complies to the Relay pagination spec into a single,
* combined list for infinite scrolling.
*
* This resolver will only work on fields that return a `Connection` GraphQL object
* type, according to the Relay pagination spec.
*
* Hint: It's not recommended to use this when you can handle infinite scrolling
* in your UI code instead.
*
* @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers#relay-pagination} for more information.
* @see {@link https://urql.dev/goto/docs/basics/ui-patterns/#infinite-scrolling} for an alternate approach.
*/
var relayPagination = (params = {}) => {
var mergeMode = params.mergeMode || 'inwards';
return (_parent, fieldArgs, cache, info) => {
var {
parentKey: entityKey,
fieldName
} = info;
var allFields = cache.inspectFields(entityKey);
var fieldInfos = allFields.filter(info => info.fieldName === fieldName);
var size = fieldInfos.length;
if (size === 0) {
return undefined;
}
var typename = null;
var startEdges = [];
var endEdges = [];
var startNodes = [];
var endNodes = [];
var pageInfo = {
...defaultPageInfo
};
for (var i = 0; i < size; i++) {
var {
fieldKey,
arguments: args
} = fieldInfos[i];
if (args === null || !compareArgs(fieldArgs, args)) {
continue;
}
var page = getPage(cache, entityKey, fieldKey);
if (page === null) {
continue;
}
if (page.nodes.length === 0 && page.edges.length === 0 && typename) {
continue;
}
if (mergeMode === 'inwards' && typeof args.last === 'number' && typeof args.first === 'number') {
var firstEdges = page.edges.slice(0, args.first + 1);
var lastEdges = page.edges.slice(-args.last);
var firstNodes = page.nodes.slice(0, args.first + 1);
var lastNodes = page.nodes.slice(-args.last);
startEdges = concatEdges(cache, startEdges, firstEdges);
endEdges = concatEdges(cache, lastEdges, endEdges);
startNodes = concatNodes(startNodes, firstNodes);
endNodes = concatNodes(lastNodes, endNodes);
pageInfo = page.pageInfo;
} else if (args.after) {
startEdges = concatEdges(cache, startEdges, page.edges);
startNodes = concatNodes(startNodes, page.nodes);
pageInfo.endCursor = page.pageInfo.endCursor;
pageInfo.hasNextPage = page.pageInfo.hasNextPage;
} else if (args.before) {
endEdges = concatEdges(cache, page.edges, endEdges);
endNodes = concatNodes(page.nodes, endNodes);
pageInfo.startCursor = page.pageInfo.startCursor;
pageInfo.hasPreviousPage = page.pageInfo.hasPreviousPage;
} else if (typeof args.last === 'number') {
endEdges = concatEdges(cache, page.edges, endEdges);
endNodes = concatNodes(page.nodes, endNodes);
pageInfo = page.pageInfo;
} else {
startEdges = concatEdges(cache, startEdges, page.edges);
startNodes = concatNodes(startNodes, page.nodes);
pageInfo = page.pageInfo;
}
if (page.pageInfo.__typename !== pageInfo.__typename) pageInfo.__typename = page.pageInfo.__typename;
if (typename !== page.__typename) typename = page.__typename;
}
if (typeof typename !== 'string') {
return undefined;
}
var hasCurrentPage = !!ensureKey(cache.resolve(entityKey, fieldName, fieldArgs));
if (!hasCurrentPage) {
if (!info.store.schema) {
return undefined;
} else {
info.partial = true;
}
}
return {
__typename: typename,
edges: mergeMode === 'inwards' ? concatEdges(cache, startEdges, endEdges) : concatEdges(cache, endEdges, startEdges),
nodes: mergeMode === 'inwards' ? concatNodes(startNodes, endNodes) : concatNodes(endNodes, startNodes),
pageInfo: {
__typename: pageInfo.__typename,
endCursor: pageInfo.endCursor,
startCursor: pageInfo.startCursor,
hasNextPage: pageInfo.hasNextPage,
hasPreviousPage: pageInfo.hasPreviousPage
}
};
};
};
/** Input parameters for the {@link simplePagination} factory. */
/** Creates a {@link Resolver} that combines pages of a primitive pagination field.
*
* @param options - A {@link PaginationParams} configuration object.
* @returns the created pagination {@link Resolver}.
*
* @remarks
* `simplePagination` is a factory that creates a {@link Resolver} that can combine
* multiple lists on a paginated field into a single, combined list for infinite
* scrolling.
*
* Hint: It's not recommended to use this when you can handle infinite scrolling
* in your UI code instead.
*
* @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers#simple-pagination} for more information.
* @see {@link https://urql.dev/goto/docs/basics/ui-patterns/#infinite-scrolling} for an alternate approach.
*/
var simplePagination = ({
offsetArgument = 'skip',
limitArgument = 'limit',
mergeMode = 'after'
} = {}) => {
var compareArgs = (fieldArgs, connectionArgs) => {
for (var key in connectionArgs) {
if (key === offsetArgument || key === limitArgument) {
continue;
} else if (!(key in fieldArgs)) {
return false;
}
var argA = fieldArgs[key];
var argB = connectionArgs[key];
if (typeof argA !== typeof argB || typeof argA !== 'object' ? argA !== argB : core.stringifyVariables(argA) !== core.stringifyVariables(argB)) {
return false;
}
}
for (var _key in fieldArgs) {
if (_key === offsetArgument || _key === limitArgument) {
continue;
}
if (!(_key in connectionArgs)) return false;
}
return true;
};
return (_parent, fieldArgs, cache, info) => {
var {
parentKey: entityKey,
fieldName
} = info;
var allFields = cache.inspectFields(entityKey);
var fieldInfos = allFields.filter(info => info.fieldName === fieldName);
var size = fieldInfos.length;
if (size === 0) {
return undefined;
}
var visited = new Set();
var result = [];
var prevOffset = null;
for (var i = 0; i < size; i++) {
var {
fieldKey,
arguments: args
} = fieldInfos[i];
if (args === null || !compareArgs(fieldArgs, args)) {
continue;
}
var links = cache.resolve(entityKey, fieldKey);
var currentOffset = args[offsetArgument];
if (links === null || links.length === 0 || typeof currentOffset !== 'number') {
continue;
}
var tempResult = [];
for (var j = 0; j < links.length; j++) {
var link = links[j];
if (visited.has(link)) continue;
tempResult.push(link);
visited.add(link);
}
if ((!prevOffset || currentOffset > prevOffset) === (mergeMode === 'after')) {
result = [...result, ...tempResult];
} else {
result = [...tempResult, ...result];
}
prevOffset = currentOffset;
}
var hasCurrentPage = cache.resolve(entityKey, fieldName, fieldArgs);
if (hasCurrentPage) {
return result;
} else if (!info.store.schema) {
return undefined;
} else {
info.partial = true;
return result;
}
};
};
exports.relayPagination = relayPagination;
exports.simplePagination = simplePagination;
//# sourceMappingURL=urql-exchange-graphcache-extras.js.map