Socket
Socket
Sign inDemoInstall

@urql/exchange-graphcache

Package Overview
Dependencies
12
Maintainers
3
Versions
290
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0-alpha.2 to 0.1.0-alpha.3

dist/types/ast/index.d.ts

648

dist/graphcache.es.js

@@ -0,32 +1,138 @@

import stringify from 'fast-json-stable-stringify';
import { formatDocument } from 'urql';
import { filter, map, merge, pipe, share, tap } from 'wonka';
import graphqlAnywhere from 'graphql-anywhere';
import stringify from 'fast-json-stable-stringify';
var getEntity = function (makeEntity) {
return function (args, context) {
if (context.operation !== 'query') {
return undefined;
}
/** Returns the name of a given node */
var getName = function (node) { return node.name.value; };
/** Returns the SelectionSet for a given inline or defined fragment node */
var fauxEntity = makeEntity(args);
var key = context.store.keyOfEntity(fauxEntity);
var entity = key !== null ? context.store.getEntity(key) : undefined;
var getSelectionSet = function (node) { return node.selectionSet.selections; };
var isOperationNode = function (node) { return node.kind === 'OperationDefinition'; };
var isFragmentNode = function (node) { return node.kind === 'FragmentDefinition'; };
if (entity === undefined) {
context.isComplete = false;
/** Evaluates a given ValueNode to a JSON value taking vars into account */
var evaluateValueNode = function (node, vars) {
switch (node.kind) {
case 'NullValue':
return null;
case 'IntValue':
return parseInt(node.value, 10);
case 'FloatValue':
return parseFloat(node.value);
case 'ListValue':
return node.values.map(function (v) { return evaluateValueNode(v, vars); });
case 'ObjectValue':
return node.fields.reduce(function (obj, field) {
obj[getName(field)] = evaluateValueNode(field.value, vars);
return obj;
}, {});
case 'Variable':
var varValue = vars[getName(node)];
return varValue !== undefined ? varValue : null;
default:
return node.value;
}
};
/** Checks whether a SelectionNode is a FieldNode */
var isFieldNode = function (node) { return node.kind === 'Field'; };
/** Checks whether a SelectionNode is an InlineFragmentNode */
var isInlineFragment = function (node) { return node.kind === 'InlineFragment'; };
/** Returns the main operation's definition */
var getMainOperation = function (doc) {
return doc.definitions.find(isOperationNode);
};
/** Returns a normalized form of variables with defaulted values */
var getNormalizedVars = function (node, input) {
if (node.variableDefinitions === undefined) {
return {};
}
var args = input ? input : {};
return node.variableDefinitions.reduce(function (vars, def) {
var name = getName(def.variable);
var value = args[name];
if (value === undefined) {
if (def.defaultValue !== undefined) {
value = evaluateValueNode(def.defaultValue, args);
} else {
return vars;
}
}
return entity;
};
vars[name] = value;
return vars;
}, {});
};
/** Returns a mapping from fragment names to their selections */
var graphql = function (resolver, ref, root, context) {
var query = ref.query;
var variables = ref.variables;
var getFragmentSelectionSets = function (doc) { return doc.definitions.filter(isFragmentNode).reduce(function (map$$1, node) {
map$$1[getName(node)] = getSelectionSet(node);
return map$$1;
}, {}); };
/** Returns either the field's name or the field's alias */
return graphqlAnywhere(resolver, query, root, context, variables);
var getFieldAlias = function (node) { return node.alias !== undefined ? node.alias.value : getName(node); };
/** Evaluates a fields arguments taking vars into account */
var getFieldArguments = function (node, vars) {
if (node.arguments === undefined || node.arguments.length === 0) {
return null;
}
return node.arguments.reduce(function (args, arg) {
args[getName(arg)] = evaluateValueNode(arg.value, vars);
return args;
}, {});
};
/** Checks whether a given SelectionNode should be ignored based on @skip or @include directives */
var shouldInclude = function (node, vars) {
if (node.directives === undefined) {
return true;
} // Finds any @include or @skip directive that forces the node to be skipped
return !node.directives.some(function (directive) {
var name = getName(directive); // Ignore other directives
var isInclude = name === 'include';
if (!isInclude && name !== 'skip') {
return false;
} // Get the first argument and expect it to be named "if"
var firstArg = directive.arguments !== undefined ? directive.arguments[0] : null;
if (firstArg === null) {
return false;
} else if (getName(firstArg) !== 'if') {
return false;
}
var value = evaluateValueNode(firstArg.value, vars);
if (typeof value !== 'boolean' && value !== null) {
return false;
} // Return whether this directive forces us to skip
// `@include(if: false)` or `@skip(if: true)`
return isInclude ? !value : !!value;
});
};
var isOperation = function (typeName) { return typeName === 'Query' || typeName === 'Mutation' || typeName === 'Subscription'; };

@@ -47,6 +153,4 @@ var keyOfEntity = function (entity) {

};
var keyForLink = function (parentKey, fieldName, args) {
var key = parentKey + "->" + fieldName;
return args ? (key + "(" + (stringify(args)) + ")") : key;
};
var keyOfField = function (fieldName, args) { return args !== null ? (fieldName + "(" + (stringify(args)) + ")") : fieldName; };
var joinKeys = function (parentKey, key) { return (parentKey + "." + key); };

@@ -61,3 +165,3 @@ var assignObjectToMap = function (map$$1, obj) {

var objectOfMap = function (map$$1) {
var res = Object.create(null);
var res = {};
map$$1.forEach(function (value, key) {

@@ -69,255 +173,378 @@ res[key] = value;

var makeCustomResolver = function (innerResolver) {
return function (fieldName, rootValue, args, context, info) {
var typeName = rootValue.__typename;
var fieldResolver = context.store.getResolver(typeName, fieldName);
/** Creates a new Store with an optional initial, serialized state */
if (fieldResolver !== undefined) {
var result = fieldResolver(args, context, info);
var create = function (initial) {
var records = new Map();
var links = new Map();
if (result !== undefined) {
return result;
}
}
if (initial !== undefined) {
assignObjectToMap(records, initial.records);
assignObjectToMap(links, initial.links);
}
return innerResolver(fieldName, rootValue, args, context, info);
return {
records: records,
links: links
};
};
/** Serializes a given Store to a plain JSON structure */
var entityOfLink = function (store, link) {
if (Array.isArray(link)) {
// @ts-ignore
return link.map(function (inner) { return entityOfLink(store, inner); });
} else if (link === null || typeof link !== 'string') {
return null;
var serialize = function (store) {
var records = objectOfMap(store.records);
var links = objectOfMap(store.links);
return {
records: records,
links: links
};
};
var find = function (store, key) {
var entity = store.records.get(key);
return entity !== undefined ? entity : null;
};
var findOrCreate = function (store, key) {
var entity = find(store, key);
if (entity !== null) {
return entity;
}
return store.getEntity(link);
var record = Object.create(null);
store.records.set(key, record);
return record;
};
var remove = function (store, key) {
store.records.delete(key);
};
var setLink = function (store, key, link) {
store.links.set(key, link);
};
var removeLink = function (store, key) {
store.links.delete(key);
};
var readLink = function (store, key) { return store.links.get(key); };
var queryResolver = function (fieldName, rootValue, args, context, info) {
if (info.isLeaf) {
return rootValue[fieldName];
}
var makeContext = function (store, request) {
var query = request.query;
var variables = request.variables;
var operation = getMainOperation(query);
var store = context.store;
var parentKey = store.keyOfEntity(rootValue);
if (parentKey === null) {
context.isComplete = false;
return null;
if (operation === undefined) {
return;
}
var link = store.getLink(keyForLink(parentKey, fieldName, args));
var dependencies = [];
var fragments = getFragmentSelectionSets(query);
var vars = getNormalizedVars(operation, variables);
var isComplete = true;
return {
dependencies: dependencies,
isComplete: isComplete,
operation: operation,
fragments: fragments,
vars: vars,
store: store
};
};
var forEachFieldNode = function (ctx, select, cb) {
var vars = ctx.vars;
var fragments = ctx.fragments;
select.forEach(function (node) {
if (!shouldInclude(node, vars)) {
// Directives instruct this node to be skipped
return;
} else if (!isFieldNode(node)) {
// This is a fragment (either inline or spread)
var fragmentSelect = isInlineFragment(node) ? getSelectionSet(node) : fragments[getName(node)]; // Recursively process the fragments' selection sets
if (link === null || link === undefined) {
var fieldValue = rootValue[fieldName];
forEachFieldNode(ctx, fragmentSelect, cb);
} else {
cb(node);
}
});
};
var merge$1 = function (dest, src) {
if (src !== null && typeof src === 'object') {
for (var key in src) {
var srcVal = src[key];
if (fieldValue === undefined) {
context.isComplete = false;
return null;
if (!(key in dest)) {
dest[key] = srcVal;
} else {
merge$1(dest[key], srcVal);
}
}
return fieldValue;
}
};
var entity = entityOfLink(store, link);
/** Reads a request entirely from the store */
if (entity === null) {
context.isComplete = false;
var query = function (store, request) {
var ctx = makeContext(store, request);
if (ctx === undefined) {
return {
isComplete: false,
dependencies: []
};
}
return entity;
};
var resolver = makeCustomResolver(queryResolver);
var query = function (store, request) {
var context = {
isComplete: true,
store: store,
operation: 'query'
};
var response = graphql(resolver, request, store.getOrCreateEntity('Query'), context);
var select = getSelectionSet(ctx.operation);
var data = readEntity(ctx, 'query', select);
return {
dependencies: store.flushTouched(),
isComplete: context.isComplete,
response: response
data: data,
isComplete: ctx.isComplete,
dependencies: ctx.dependencies
};
};
var isLinkableEntity = function (store, x) {
if (Array.isArray(x)) {
// @ts-ignore
return x.every(function (inner) { return isLinkableEntity(store, inner); });
}
var readEntity = function (ctx, key, select) {
var store = ctx.store;
var entity = find(store, key);
return x === null || typeof x === 'object' && store.keyOfEntity(x) !== null;
}; // Transforms a fieldValue to keys of entities
var linkOfEntity = function (store, x) {
if (Array.isArray(x)) {
// @ts-ignore
return x.map(function (inner) { return linkOfEntity(store, inner); });
} else if (x === null || typeof x !== 'object') {
if (entity === null) {
// Cache Incomplete: A missing entity for a key means it wasn't cached
ctx.isComplete = false;
return null;
} else if (key !== 'query') {
ctx.dependencies.push(key);
}
return store.keyOfEntity(x);
var data = Object.create(null);
readSelection(ctx, entity, key, data, select);
return data;
};
var writeResolver = function (fieldName, rootValue, args, ref, info) {
var store = ref.store;
var readSelection = function (ctx, entity, key, data, select) {
var store = ctx.store;
var vars = ctx.vars;
var fragments = ctx.fragments;
select.forEach(function (node) {
if (!shouldInclude(node, vars)) {
// Directives instruct this node to be skipped
return;
} else if (!isFieldNode(node)) {
// This is a fragment (either inline or spread)
var fragmentSelect = isInlineFragment(node) ? getSelectionSet(node) : fragments[getName(node)]; // Recursively process the fragments' selection sets
var fieldValue = rootValue[info.resultKey || fieldName];
var parentKey = store.keyOfEntity(rootValue);
var fragmentData = Object.create(null);
readSelection(ctx, entity, key, fragmentData, fragmentSelect);
merge$1(data, fragmentData);
} else {
var fieldName = getName(node); // The field's key can include arguments if it has any
if (parentKey === null) {
return null;
} else if (parentKey === 'Mutation' || parentKey === 'Subscription') {
// We do traverse but we don't store Mutation & Subscription themselves
return fieldValue;
} else if (fieldValue === null || fieldValue === undefined) {
// Clear stored field since value is null
store.writeEntityValue(parentKey, fieldName, null);
return null;
} else if (info.isLeaf || typeof fieldValue !== 'object') {
// Write leaf / scalar value to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
return null;
} // Determine if this is a link and not a scalar
var fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
var fieldValue = entity[fieldKey];
var fieldAlias = getFieldAlias(node);
var childFieldKey = joinKeys(key, fieldKey);
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
var shouldCreateLink = isLinkableEntity(store, fieldValue);
if (node.selectionSet === undefined || fieldValue !== null) {
// Cache Incomplete: An undefined field value means it wasn't cached
ctx.isComplete = fieldValue !== undefined;
data[fieldAlias] = fieldValue === undefined ? null : fieldValue;
} else {
// null values mean that a field might be linked to other entities
var ref = node.selectionSet;
var fieldSelect = ref.selections;
var link = readLink(store, childFieldKey); // Cache Incomplete: A missing link for a field means it's not cached
if (!shouldCreateLink) {
// Write object-like scalar to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
if (link === undefined) {
ctx.isComplete = false;
data[fieldAlias] = null;
} else {
data[fieldAlias] = readField(ctx, link, fieldSelect);
}
}
}
});
};
var readField = function (ctx, link, select) {
if (Array.isArray(link)) {
// @ts-ignore: Link cannot be expressed as a recursive type
return link.map(function (childLink) { return readField(ctx, childLink, select); });
} else if (link === null) {
return null;
} // Clear stored field since it's not a scalar
}
return readEntity(ctx, link, select);
};
store.writeEntityValue(parentKey, fieldName, undefined); // Write link to store and keep traversing
/** Writes a request given its response to the store */
var link = linkOfEntity(store, fieldValue);
store.writeLink(keyForLink(parentKey, fieldName, args), link);
return fieldValue;
};
var write = function (store, request, data) {
var ctx = makeContext(store, request);
var resolver$1 = makeCustomResolver(writeResolver);
if (ctx === undefined) {
return {
isComplete: false,
dependencies: []
};
}
var write = function (store, request, response) {
graphql(resolver$1, request, response, {
isComplete: true,
store: store,
operation: 'write'
});
var dependencies = store.flushTouched();
var operation = ctx.operation;
var select = getSelectionSet(operation);
var operationName = operation.operation;
if (operationName === 'subscription' || operationName === 'mutation') {
writeRoot(ctx, data, select);
} else {
writeEntity(ctx, operationName, data, select);
}
return {
isComplete: true,
dependencies: dependencies
dependencies: ctx.dependencies
};
};
var Store = function Store(ref) {
var initial = ref.initial;
var keyExtractor = ref.keyExtractor;
var resolvers = ref.resolvers;
var writeEntity = function (ctx, key, data, select) {
var store = ctx.store;
var entity = findOrCreate(store, key);
this.touched = [];
this.records = new Map();
this.links = new Map();
this.resolvers = resolvers || {};
if (keyExtractor !== undefined) {
this.keyOfEntity = function (entity) {
var key = keyExtractor(entity);
return key !== undefined ? key : keyOfEntity(entity);
};
} else {
this.keyOfEntity = keyOfEntity;
if (key !== 'query') {
ctx.dependencies.push(key);
}
if (initial !== undefined) {
assignObjectToMap(this.records, initial.records);
assignObjectToMap(this.links, initial.links);
}
writeSelection(ctx, entity, key, data, select);
};
Store.prototype.getResolver = function getResolver (typeName, fieldName) {
if (typeName === null || typeName === undefined) {
return undefined;
}
var writeSelection = function (ctx, entity, key, data, select) {
forEachFieldNode(ctx, select, function (node) {
var store = ctx.store;
var vars = ctx.vars;
var fieldName = getName(node);
var fieldValue = data[getFieldAlias(node)]; // The field's key can include arguments if it has any
var typeResolvers = this.resolvers[typeName];
return typeResolvers !== undefined ? typeResolvers[fieldName] : undefined;
};
var fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
var childFieldKey = joinKeys(key, fieldKey);
Store.prototype.getEntity = function getEntity (key) {
if (!isOperation(key)) {
this.touched.push(key);
}
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
var entity = this.records.get(key);
return entity !== undefined ? entity : null;
if (node.selectionSet === undefined || fieldValue === null || typeof fieldValue !== 'object') {
// This is a leaf node, so we're setting the field's value directly
entity[fieldKey] = fieldValue; // Remove any links that might've existed before for this field
removeLink(store, childFieldKey);
} else {
// Ensure that this key exists on the entity and that previous values are thrown away
entity[fieldKey] = null; // Process the field and write links for the child entities that have been written
var ref = node.selectionSet;
var fieldSelect = ref.selections;
var link = writeField(ctx, childFieldKey, fieldValue, fieldSelect);
setLink(store, childFieldKey, link);
}
});
};
Store.prototype.getOrCreateEntity = function getOrCreateEntity (key) {
var prev = this.records.get(key);
var writeField = function (ctx, parentFieldKey, data, select) {
if (Array.isArray(data)) {
return data.map(function (item, index) {
// Append the current index to the parentFieldKey fallback
var indexKey = joinKeys(parentFieldKey, ("" + index)); // Recursively write array data
if (prev !== undefined && prev !== null) {
return prev;
}
var links = writeField(ctx, indexKey, item, select); // Link cannot be expressed as a recursive type
var entity = Object.create(null);
this.records.set(key, entity);
return entity;
};
return links;
});
} else if (data === null) {
return null;
} // Write entity to key that falls back to the given parentFieldKey
Store.prototype.writeEntityValue = function writeEntityValue (key, prop, val) {
if (!isOperation(key)) {
this.touched.push(key);
}
var entity = this.getOrCreateEntity(key);
var entityKey = keyOfEntity(data);
var key = entityKey !== null ? entityKey : parentFieldKey;
writeEntity(ctx, key, data, select);
return key;
}; // This is like writeSelection but assumes no parent entity exists
if (val === undefined) {
delete entity[prop];
} else {
entity[prop] = val;
var writeRoot = function (ctx, data, select) {
forEachFieldNode(ctx, select, function (node) {
var fieldValue = data[getFieldAlias(node)];
if (node.selectionSet !== undefined && typeof fieldValue === 'object') {
var ref = node.selectionSet;
var fieldSelect = ref.selections;
writeRootField(ctx, fieldValue, fieldSelect);
}
});
}; // This is like wroteField but doesn't fall back to a generated key
var writeRootField = function (ctx, data, select) {
if (Array.isArray(data)) {
return data.map(function (item) { return writeRootField(ctx, item, select); });
} else if (data === null) {
return;
} // Write entity to key that falls back to the given parentFieldKey
var entityKey = keyOfEntity(data);
if (entityKey !== null) {
writeEntity(ctx, entityKey, data, select);
}
};
Store.prototype.getLink = function getLink (key) {
this.touched.push(key);
var link = this.links.get(key);
return link !== undefined ? link : null;
var gc = function (store) {
var visitedLinks = new Set();
var visitedRecords = new Set();
var ctx = {
store: store,
visitedLinks: visitedLinks,
visitedRecords: visitedRecords
};
walkEntity(ctx, 'query');
store.records.forEach(function (_entity, key) {
if (!visitedRecords.has(key)) {
remove(store, key);
}
});
store.links.forEach(function (_link, key) {
if (!visitedLinks.has(key)) {
removeLink(store, key);
}
});
};
Store.prototype.writeLink = function writeLink (key, link) {
this.touched.push(key);
var walkEntity = function (ctx, key) {
var store = ctx.store;
var visitedRecords = ctx.visitedRecords;
var visitedLinks = ctx.visitedLinks;
var entity = find(store, key);
if (link === null) {
this.links.delete(key);
} else {
this.links.set(key, link);
if (entity !== null && !visitedRecords.has(key)) {
visitedRecords.add(key);
for (var fieldKey in entity) {
var value = entity[key];
if (value === null) {
var childFieldKey = joinKeys(key, fieldKey);
var link = readLink(store, childFieldKey);
if (link !== undefined && !visitedLinks.has(childFieldKey)) {
visitedLinks.add(childFieldKey);
walkLink(ctx, link);
}
}
}
}
};
Store.prototype.toJSON = function toJSON () {
return {
records: objectOfMap(this.records),
links: objectOfMap(this.links)
};
var walkLink = function (ctx, link) {
if (Array.isArray(link)) {
link.forEach(function (childLink) { return walkLink(ctx, childLink); });
} else if (link !== null) {
walkEntity(ctx, link);
}
};
Store.prototype.flushTouched = function flushTouched () {
var touched = this.touched.filter(function (key, i, arr) {
return arr.indexOf(key) === i;
});
this.touched = [];
return touched;
};
var addTypeNames = function (op) { return (Object.assign({}, op,

@@ -342,5 +569,5 @@ {query: formatDocument(op.query)})); }; // Retrieves the requestPolicy from an operation

var toNetworkOnly = function (operation) { return (Object.assign({}, operation,
var toRequestPolicy = function (operation, requestPolicy) { return (Object.assign({}, operation,
{context: Object.assign({}, operation.context,
{requestPolicy: 'network-only'})})); };
{requestPolicy: requestPolicy})})); };

@@ -351,3 +578,3 @@ var cacheExchange = function (opts) { return function (ref) {

var store = new Store(opts);
var store = create(opts.initial);
var ops = new Map();

@@ -386,3 +613,4 @@ var deps = Object.create(null); // This accepts an array of dependencies and reexecutes all known operations

if (!ops.has(op.key)) {
ops.set(op.key, op);
var isNetworkOnly = op.context.requestPolicy === 'network-only';
ops.set(op.key, isNetworkOnly ? toRequestPolicy(op, 'cache-and-network') : op);
}

@@ -406,3 +634,3 @@ });

isComplete: isComplete,
data: res.response
data: res.data
};

@@ -443,3 +671,3 @@ }; // Take any OperationResult and update the cache with it

if (policy === 'cache-and-network') {
var networkOnly = toNetworkOnly(operation);
var networkOnly = toRequestPolicy(operation, 'network-only');
client.reexecuteOperation(networkOnly);

@@ -455,3 +683,3 @@ }

export { cacheExchange, getEntity };
export { create, serialize, cacheExchange, query, write, gc };
//# sourceMappingURL=graphcache.es.js.map

@@ -1,1 +0,1 @@

import{formatDocument}from"urql";import{filter,map,merge,pipe,share,tap}from"wonka";import graphqlAnywhere from"graphql-anywhere";import stringify from"fast-json-stable-stringify";var getEntity=function(makeEntity){return function(args,context){if(context.operation!=="query"){return undefined}var fauxEntity=makeEntity(args);var key=context.store.keyOfEntity(fauxEntity);var entity=key!==null?context.store.getEntity(key):undefined;if(entity===undefined){context.isComplete=false;return null}return entity}};var graphql=function(resolver,ref,root,context){var query=ref.query;var variables=ref.variables;return graphqlAnywhere(resolver,query,root,context,variables)};var isOperation=function(typeName){return typeName==="Query"||typeName==="Mutation"||typeName==="Subscription"};var keyOfEntity=function(entity){var typeName=entity.__typename;var id=entity.id===undefined?entity._id:entity.id;if(typeName===undefined||typeName===null){return null}else if(isOperation(typeName)){return typeName}else if(id===null||id===undefined){return null}return typeName+":"+id};var keyForLink=function(parentKey,fieldName,args){var key=parentKey+"->"+fieldName;return args?key+"("+stringify(args)+")":key};var assignObjectToMap=function(map$$1,obj){for(var key in obj){if(obj.hasOwnProperty(key)){map$$1.set(key,obj[key])}}};var objectOfMap=function(map$$1){var res=Object.create(null);map$$1.forEach(function(value,key){res[key]=value});return res};var makeCustomResolver=function(innerResolver){return function(fieldName,rootValue,args,context,info){var typeName=rootValue.__typename;var fieldResolver=context.store.getResolver(typeName,fieldName);if(fieldResolver!==undefined){var result=fieldResolver(args,context,info);if(result!==undefined){return result}}return innerResolver(fieldName,rootValue,args,context,info)}};var entityOfLink=function(store,link){if(Array.isArray(link)){return link.map(function(inner){return entityOfLink(store,inner)})}else if(link===null||typeof link!=="string"){return null}return store.getEntity(link)};var queryResolver=function(fieldName,rootValue,args,context,info){if(info.isLeaf){return rootValue[fieldName]}var store=context.store;var parentKey=store.keyOfEntity(rootValue);if(parentKey===null){context.isComplete=false;return null}var link=store.getLink(keyForLink(parentKey,fieldName,args));if(link===null||link===undefined){var fieldValue=rootValue[fieldName];if(fieldValue===undefined){context.isComplete=false;return null}return fieldValue}var entity=entityOfLink(store,link);if(entity===null){context.isComplete=false}return entity};var resolver=makeCustomResolver(queryResolver);var query=function(store,request){var context={isComplete:true,store:store,operation:"query"};var response=graphql(resolver,request,store.getOrCreateEntity("Query"),context);return{dependencies:store.flushTouched(),isComplete:context.isComplete,response:response}};var isLinkableEntity=function(store,x){if(Array.isArray(x)){return x.every(function(inner){return isLinkableEntity(store,inner)})}return x===null||typeof x==="object"&&store.keyOfEntity(x)!==null};var linkOfEntity=function(store,x){if(Array.isArray(x)){return x.map(function(inner){return linkOfEntity(store,inner)})}else if(x===null||typeof x!=="object"){return null}return store.keyOfEntity(x)};var writeResolver=function(fieldName,rootValue,args,ref,info){var store=ref.store;var fieldValue=rootValue[info.resultKey||fieldName];var parentKey=store.keyOfEntity(rootValue);if(parentKey===null){return null}else if(parentKey==="Mutation"||parentKey==="Subscription"){return fieldValue}else if(fieldValue===null||fieldValue===undefined){store.writeEntityValue(parentKey,fieldName,null);return null}else if(info.isLeaf||typeof fieldValue!=="object"){store.writeEntityValue(parentKey,fieldName,fieldValue);return null}var shouldCreateLink=isLinkableEntity(store,fieldValue);if(!shouldCreateLink){store.writeEntityValue(parentKey,fieldName,fieldValue);return null}store.writeEntityValue(parentKey,fieldName,undefined);var link=linkOfEntity(store,fieldValue);store.writeLink(keyForLink(parentKey,fieldName,args),link);return fieldValue};var resolver$1=makeCustomResolver(writeResolver);var write=function(store,request,response){graphql(resolver$1,request,response,{isComplete:true,store:store,operation:"write"});var dependencies=store.flushTouched();return{isComplete:true,dependencies:dependencies}};var Store=function Store(ref){var initial=ref.initial;var keyExtractor=ref.keyExtractor;var resolvers=ref.resolvers;this.touched=[];this.records=new Map;this.links=new Map;this.resolvers=resolvers||{};if(keyExtractor!==undefined){this.keyOfEntity=function(entity){var key=keyExtractor(entity);return key!==undefined?key:keyOfEntity(entity)}}else{this.keyOfEntity=keyOfEntity}if(initial!==undefined){assignObjectToMap(this.records,initial.records);assignObjectToMap(this.links,initial.links)}};Store.prototype.getResolver=function getResolver(typeName,fieldName){if(typeName===null||typeName===undefined){return undefined}var typeResolvers=this.resolvers[typeName];return typeResolvers!==undefined?typeResolvers[fieldName]:undefined};Store.prototype.getEntity=function getEntity(key){if(!isOperation(key)){this.touched.push(key)}var entity=this.records.get(key);return entity!==undefined?entity:null};Store.prototype.getOrCreateEntity=function getOrCreateEntity(key){var prev=this.records.get(key);if(prev!==undefined&&prev!==null){return prev}var entity=Object.create(null);this.records.set(key,entity);return entity};Store.prototype.writeEntityValue=function writeEntityValue(key,prop,val){if(!isOperation(key)){this.touched.push(key)}var entity=this.getOrCreateEntity(key);if(val===undefined){delete entity[prop]}else{entity[prop]=val}};Store.prototype.getLink=function getLink(key){this.touched.push(key);var link=this.links.get(key);return link!==undefined?link:null};Store.prototype.writeLink=function writeLink(key,link){this.touched.push(key);if(link===null){this.links.delete(key)}else{this.links.set(key,link)}};Store.prototype.toJSON=function toJSON(){return{records:objectOfMap(this.records),links:objectOfMap(this.links)}};Store.prototype.flushTouched=function flushTouched(){var touched=this.touched.filter(function(key,i,arr){return arr.indexOf(key)===i});this.touched=[];return touched};var addTypeNames=function(op){return Object.assign({},op,{query:formatDocument(op.query)})};var getRequestPolicy=function(op){return op.context.requestPolicy};var isQueryOperation=function(op){var policy=getRequestPolicy(op);return op.operationName==="query"&&(policy==="cache-and-network"||policy==="cache-first"||policy==="cache-only")};var isCacheableQuery=function(op){var policy=getRequestPolicy(op);return isQueryOperation(op)&&(policy==="cache-and-network"||policy==="cache-first"||policy==="cache-only")};var toNetworkOnly=function(operation){return Object.assign({},operation,{context:Object.assign({},operation.context,{requestPolicy:"network-only"})})};var cacheExchange=function(opts){return function(ref){var forward=ref.forward;var client=ref.client;var store=new Store(opts);var ops=new Map;var deps=Object.create(null);var processDependencies=function(triggerOp,dependencies){var pendingOperations=new Set;dependencies.forEach(function(dep){var keys=deps[dep];if(keys!==undefined){deps[dep]=[];keys.forEach(function(key){return pendingOperations.add(key)})}});pendingOperations.forEach(function(key){if(key!==triggerOp.key){var op=ops.get(key);ops.delete(key);client.reexecuteOperation(op)}})};var updateDependencies=function(op,dependencies){dependencies.forEach(function(dep){var keys=deps[dep]||(deps[dep]=[]);keys.push(op.key);if(!ops.has(op.key)){ops.set(op.key,op)}})};var operationResultFromCache=function(operation){var policy=getRequestPolicy(operation);var res=query(store,operation);var isComplete=policy==="cache-only"||res.isComplete;if(isComplete){updateDependencies(operation,res.dependencies)}return{operation:operation,isComplete:isComplete,data:res.response}};var updateCacheWithResult=function(ref){var error=ref.error;var data=ref.data;var operation=ref.operation;if((error===undefined||error.networkError===undefined)&&data!==null&&data!==undefined){var ref$1=write(store,operation,data);var dependencies=ref$1.dependencies;processDependencies(operation,dependencies);if(isQueryOperation(operation)){updateDependencies(operation,dependencies)}}};return function(ops$){var sharedOps$=pipe(ops$,map(addTypeNames),share);var cache$=pipe(sharedOps$,filter(function(op){return isCacheableQuery(op)}),map(operationResultFromCache),share);var cacheOps$=pipe(cache$,filter(function(res){return!res.isComplete}),map(function(res){return res.operation}));var cacheResult$=pipe(cache$,filter(function(res){return res.isComplete}),tap(function(ref){var operation=ref.operation;var policy=getRequestPolicy(operation);if(policy==="cache-and-network"){var networkOnly=toNetworkOnly(operation);client.reexecuteOperation(networkOnly)}}));var result$=pipe(forward(merge([pipe(sharedOps$,filter(function(op){return!isCacheableQuery(op)})),cacheOps$])),tap(updateCacheWithResult));return merge([result$,cacheResult$])}}};export{cacheExchange,getEntity};
import stringify from"fast-json-stable-stringify";import{formatDocument}from"urql";import{filter,map,merge,pipe,share,tap}from"wonka";var getName=function(node){return node.name.value};var getSelectionSet=function(node){return node.selectionSet.selections};var isOperationNode=function(node){return node.kind==="OperationDefinition"};var isFragmentNode=function(node){return node.kind==="FragmentDefinition"};var evaluateValueNode=function(node,vars){switch(node.kind){case"NullValue":return null;case"IntValue":return parseInt(node.value,10);case"FloatValue":return parseFloat(node.value);case"ListValue":return node.values.map(function(v){return evaluateValueNode(v,vars)});case"ObjectValue":return node.fields.reduce(function(obj,field){obj[getName(field)]=evaluateValueNode(field.value,vars);return obj},{});case"Variable":var varValue=vars[getName(node)];return varValue!==undefined?varValue:null;default:return node.value}};var isFieldNode=function(node){return node.kind==="Field"};var isInlineFragment=function(node){return node.kind==="InlineFragment"};var getMainOperation=function(doc){return doc.definitions.find(isOperationNode)};var getNormalizedVars=function(node,input){if(node.variableDefinitions===undefined){return{}}var args=input?input:{};return node.variableDefinitions.reduce(function(vars,def){var name=getName(def.variable);var value=args[name];if(value===undefined){if(def.defaultValue!==undefined){value=evaluateValueNode(def.defaultValue,args)}else{return vars}}vars[name]=value;return vars},{})};var getFragmentSelectionSets=function(doc){return doc.definitions.filter(isFragmentNode).reduce(function(map$$1,node){map$$1[getName(node)]=getSelectionSet(node);return map$$1},{})};var getFieldAlias=function(node){return node.alias!==undefined?node.alias.value:getName(node)};var getFieldArguments=function(node,vars){if(node.arguments===undefined||node.arguments.length===0){return null}return node.arguments.reduce(function(args,arg){args[getName(arg)]=evaluateValueNode(arg.value,vars);return args},{})};var shouldInclude=function(node,vars){if(node.directives===undefined){return true}return!node.directives.some(function(directive){var name=getName(directive);var isInclude=name==="include";if(!isInclude&&name!=="skip"){return false}var firstArg=directive.arguments!==undefined?directive.arguments[0]:null;if(firstArg===null){return false}else if(getName(firstArg)!=="if"){return false}var value=evaluateValueNode(firstArg.value,vars);if(typeof value!=="boolean"&&value!==null){return false}return isInclude?!value:!!value})};var isOperation=function(typeName){return typeName==="Query"||typeName==="Mutation"||typeName==="Subscription"};var keyOfEntity=function(entity){var typeName=entity.__typename;var id=entity.id===undefined?entity._id:entity.id;if(typeName===undefined||typeName===null){return null}else if(isOperation(typeName)){return typeName}else if(id===null||id===undefined){return null}return typeName+":"+id};var keyOfField=function(fieldName,args){return args!==null?fieldName+"("+stringify(args)+")":fieldName};var joinKeys=function(parentKey,key){return parentKey+"."+key};var assignObjectToMap=function(map$$1,obj){for(var key in obj){if(obj.hasOwnProperty(key)){map$$1.set(key,obj[key])}}};var objectOfMap=function(map$$1){var res={};map$$1.forEach(function(value,key){res[key]=value});return res};var create=function(initial){var records=new Map;var links=new Map;if(initial!==undefined){assignObjectToMap(records,initial.records);assignObjectToMap(links,initial.links)}return{records:records,links:links}};var serialize=function(store){var records=objectOfMap(store.records);var links=objectOfMap(store.links);return{records:records,links:links}};var find=function(store,key){var entity=store.records.get(key);return entity!==undefined?entity:null};var findOrCreate=function(store,key){var entity=find(store,key);if(entity!==null){return entity}var record=Object.create(null);store.records.set(key,record);return record};var remove=function(store,key){store.records.delete(key)};var setLink=function(store,key,link){store.links.set(key,link)};var removeLink=function(store,key){store.links.delete(key)};var readLink=function(store,key){return store.links.get(key)};var makeContext=function(store,request){var query=request.query;var variables=request.variables;var operation=getMainOperation(query);if(operation===undefined){return}var dependencies=[];var fragments=getFragmentSelectionSets(query);var vars=getNormalizedVars(operation,variables);var isComplete=true;return{dependencies:dependencies,isComplete:isComplete,operation:operation,fragments:fragments,vars:vars,store:store}};var forEachFieldNode=function(ctx,select,cb){var vars=ctx.vars;var fragments=ctx.fragments;select.forEach(function(node){if(!shouldInclude(node,vars)){return}else if(!isFieldNode(node)){var fragmentSelect=isInlineFragment(node)?getSelectionSet(node):fragments[getName(node)];forEachFieldNode(ctx,fragmentSelect,cb)}else{cb(node)}})};var merge$1=function(dest,src){if(src!==null&&typeof src==="object"){for(var key in src){var srcVal=src[key];if(!(key in dest)){dest[key]=srcVal}else{merge$1(dest[key],srcVal)}}}};var query=function(store,request){var ctx=makeContext(store,request);if(ctx===undefined){return{isComplete:false,dependencies:[]}}var select=getSelectionSet(ctx.operation);var data=readEntity(ctx,"query",select);return{data:data,isComplete:ctx.isComplete,dependencies:ctx.dependencies}};var readEntity=function(ctx,key,select){var store=ctx.store;var entity=find(store,key);if(entity===null){ctx.isComplete=false;return null}else if(key!=="query"){ctx.dependencies.push(key)}var data=Object.create(null);readSelection(ctx,entity,key,data,select);return data};var readSelection=function(ctx,entity,key,data,select){var store=ctx.store;var vars=ctx.vars;var fragments=ctx.fragments;select.forEach(function(node){if(!shouldInclude(node,vars)){return}else if(!isFieldNode(node)){var fragmentSelect=isInlineFragment(node)?getSelectionSet(node):fragments[getName(node)];var fragmentData=Object.create(null);readSelection(ctx,entity,key,fragmentData,fragmentSelect);merge$1(data,fragmentData)}else{var fieldName=getName(node);var fieldKey=keyOfField(fieldName,getFieldArguments(node,vars));var fieldValue=entity[fieldKey];var fieldAlias=getFieldAlias(node);var childFieldKey=joinKeys(key,fieldKey);if(key==="query"){ctx.dependencies.push(childFieldKey)}if(node.selectionSet===undefined||fieldValue!==null){ctx.isComplete=fieldValue!==undefined;data[fieldAlias]=fieldValue===undefined?null:fieldValue}else{var ref=node.selectionSet;var fieldSelect=ref.selections;var link=readLink(store,childFieldKey);if(link===undefined){ctx.isComplete=false;data[fieldAlias]=null}else{data[fieldAlias]=readField(ctx,link,fieldSelect)}}}})};var readField=function(ctx,link,select){if(Array.isArray(link)){return link.map(function(childLink){return readField(ctx,childLink,select)})}else if(link===null){return null}return readEntity(ctx,link,select)};var write=function(store,request,data){var ctx=makeContext(store,request);if(ctx===undefined){return{isComplete:false,dependencies:[]}}var operation=ctx.operation;var select=getSelectionSet(operation);var operationName=operation.operation;if(operationName==="subscription"||operationName==="mutation"){writeRoot(ctx,data,select)}else{writeEntity(ctx,operationName,data,select)}return{isComplete:true,dependencies:ctx.dependencies}};var writeEntity=function(ctx,key,data,select){var store=ctx.store;var entity=findOrCreate(store,key);if(key!=="query"){ctx.dependencies.push(key)}writeSelection(ctx,entity,key,data,select)};var writeSelection=function(ctx,entity,key,data,select){forEachFieldNode(ctx,select,function(node){var store=ctx.store;var vars=ctx.vars;var fieldName=getName(node);var fieldValue=data[getFieldAlias(node)];var fieldKey=keyOfField(fieldName,getFieldArguments(node,vars));var childFieldKey=joinKeys(key,fieldKey);if(key==="query"){ctx.dependencies.push(childFieldKey)}if(node.selectionSet===undefined||fieldValue===null||typeof fieldValue!=="object"){entity[fieldKey]=fieldValue;removeLink(store,childFieldKey)}else{entity[fieldKey]=null;var ref=node.selectionSet;var fieldSelect=ref.selections;var link=writeField(ctx,childFieldKey,fieldValue,fieldSelect);setLink(store,childFieldKey,link)}})};var writeField=function(ctx,parentFieldKey,data,select){if(Array.isArray(data)){return data.map(function(item,index){var indexKey=joinKeys(parentFieldKey,""+index);var links=writeField(ctx,indexKey,item,select);return links})}else if(data===null){return null}var entityKey=keyOfEntity(data);var key=entityKey!==null?entityKey:parentFieldKey;writeEntity(ctx,key,data,select);return key};var writeRoot=function(ctx,data,select){forEachFieldNode(ctx,select,function(node){var fieldValue=data[getFieldAlias(node)];if(node.selectionSet!==undefined&&typeof fieldValue==="object"){var ref=node.selectionSet;var fieldSelect=ref.selections;writeRootField(ctx,fieldValue,fieldSelect)}})};var writeRootField=function(ctx,data,select){if(Array.isArray(data)){return data.map(function(item){return writeRootField(ctx,item,select)})}else if(data===null){return}var entityKey=keyOfEntity(data);if(entityKey!==null){writeEntity(ctx,entityKey,data,select)}};var gc=function(store){var visitedLinks=new Set;var visitedRecords=new Set;var ctx={store:store,visitedLinks:visitedLinks,visitedRecords:visitedRecords};walkEntity(ctx,"query");store.records.forEach(function(_entity,key){if(!visitedRecords.has(key)){remove(store,key)}});store.links.forEach(function(_link,key){if(!visitedLinks.has(key)){removeLink(store,key)}})};var walkEntity=function(ctx,key){var store=ctx.store;var visitedRecords=ctx.visitedRecords;var visitedLinks=ctx.visitedLinks;var entity=find(store,key);if(entity!==null&&!visitedRecords.has(key)){visitedRecords.add(key);for(var fieldKey in entity){var value=entity[key];if(value===null){var childFieldKey=joinKeys(key,fieldKey);var link=readLink(store,childFieldKey);if(link!==undefined&&!visitedLinks.has(childFieldKey)){visitedLinks.add(childFieldKey);walkLink(ctx,link)}}}}};var walkLink=function(ctx,link){if(Array.isArray(link)){link.forEach(function(childLink){return walkLink(ctx,childLink)})}else if(link!==null){walkEntity(ctx,link)}};var addTypeNames=function(op){return Object.assign({},op,{query:formatDocument(op.query)})};var getRequestPolicy=function(op){return op.context.requestPolicy};var isQueryOperation=function(op){var policy=getRequestPolicy(op);return op.operationName==="query"&&(policy==="cache-and-network"||policy==="cache-first"||policy==="cache-only")};var isCacheableQuery=function(op){var policy=getRequestPolicy(op);return isQueryOperation(op)&&(policy==="cache-and-network"||policy==="cache-first"||policy==="cache-only")};var toRequestPolicy=function(operation,requestPolicy){return Object.assign({},operation,{context:Object.assign({},operation.context,{requestPolicy:requestPolicy})})};var cacheExchange=function(opts){return function(ref){var forward=ref.forward;var client=ref.client;var store=create(opts.initial);var ops=new Map;var deps=Object.create(null);var processDependencies=function(triggerOp,dependencies){var pendingOperations=new Set;dependencies.forEach(function(dep){var keys=deps[dep];if(keys!==undefined){deps[dep]=[];keys.forEach(function(key){return pendingOperations.add(key)})}});pendingOperations.forEach(function(key){if(key!==triggerOp.key){var op=ops.get(key);ops.delete(key);client.reexecuteOperation(op)}})};var updateDependencies=function(op,dependencies){dependencies.forEach(function(dep){var keys=deps[dep]||(deps[dep]=[]);keys.push(op.key);if(!ops.has(op.key)){var isNetworkOnly=op.context.requestPolicy==="network-only";ops.set(op.key,isNetworkOnly?toRequestPolicy(op,"cache-and-network"):op)}})};var operationResultFromCache=function(operation){var policy=getRequestPolicy(operation);var res=query(store,operation);var isComplete=policy==="cache-only"||res.isComplete;if(isComplete){updateDependencies(operation,res.dependencies)}return{operation:operation,isComplete:isComplete,data:res.data}};var updateCacheWithResult=function(ref){var error=ref.error;var data=ref.data;var operation=ref.operation;if((error===undefined||error.networkError===undefined)&&data!==null&&data!==undefined){var ref$1=write(store,operation,data);var dependencies=ref$1.dependencies;processDependencies(operation,dependencies);if(isQueryOperation(operation)){updateDependencies(operation,dependencies)}}};return function(ops$){var sharedOps$=pipe(ops$,map(addTypeNames),share);var cache$=pipe(sharedOps$,filter(function(op){return isCacheableQuery(op)}),map(operationResultFromCache),share);var cacheOps$=pipe(cache$,filter(function(res){return!res.isComplete}),map(function(res){return res.operation}));var cacheResult$=pipe(cache$,filter(function(res){return res.isComplete}),tap(function(ref){var operation=ref.operation;var policy=getRequestPolicy(operation);if(policy==="cache-and-network"){var networkOnly=toRequestPolicy(operation,"network-only");client.reexecuteOperation(networkOnly)}}));var result$=pipe(forward(merge([pipe(sharedOps$,filter(function(op){return!isCacheableQuery(op)})),cacheOps$])),tap(updateCacheWithResult));return merge([result$,cacheResult$])}}};export{create,serialize,cacheExchange,query,write,gc};
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var stringify = _interopDefault(require('fast-json-stable-stringify'));
var urql = require('urql');
var wonka = require('wonka');
var graphqlAnywhere = _interopDefault(require('graphql-anywhere'));
var stringify = _interopDefault(require('fast-json-stable-stringify'));
var getEntity = function (makeEntity) {
return function (args, context) {
if (context.operation !== 'query') {
return undefined;
}
/** Returns the name of a given node */
var getName = function (node) { return node.name.value; };
/** Returns the SelectionSet for a given inline or defined fragment node */
var fauxEntity = makeEntity(args);
var key = context.store.keyOfEntity(fauxEntity);
var entity = key !== null ? context.store.getEntity(key) : undefined;
var getSelectionSet = function (node) { return node.selectionSet.selections; };
var isOperationNode = function (node) { return node.kind === 'OperationDefinition'; };
var isFragmentNode = function (node) { return node.kind === 'FragmentDefinition'; };
if (entity === undefined) {
context.isComplete = false;
/** Evaluates a given ValueNode to a JSON value taking vars into account */
var evaluateValueNode = function (node, vars) {
switch (node.kind) {
case 'NullValue':
return null;
case 'IntValue':
return parseInt(node.value, 10);
case 'FloatValue':
return parseFloat(node.value);
case 'ListValue':
return node.values.map(function (v) { return evaluateValueNode(v, vars); });
case 'ObjectValue':
return node.fields.reduce(function (obj, field) {
obj[getName(field)] = evaluateValueNode(field.value, vars);
return obj;
}, {});
case 'Variable':
var varValue = vars[getName(node)];
return varValue !== undefined ? varValue : null;
default:
return node.value;
}
};
/** Checks whether a SelectionNode is a FieldNode */
var isFieldNode = function (node) { return node.kind === 'Field'; };
/** Checks whether a SelectionNode is an InlineFragmentNode */
var isInlineFragment = function (node) { return node.kind === 'InlineFragment'; };
/** Returns the main operation's definition */
var getMainOperation = function (doc) {
return doc.definitions.find(isOperationNode);
};
/** Returns a normalized form of variables with defaulted values */
var getNormalizedVars = function (node, input) {
if (node.variableDefinitions === undefined) {
return {};
}
var args = input ? input : {};
return node.variableDefinitions.reduce(function (vars, def) {
var name = getName(def.variable);
var value = args[name];
if (value === undefined) {
if (def.defaultValue !== undefined) {
value = evaluateValueNode(def.defaultValue, args);
} else {
return vars;
}
}
return entity;
};
vars[name] = value;
return vars;
}, {});
};
/** Returns a mapping from fragment names to their selections */
var graphql = function (resolver, ref, root, context) {
var query = ref.query;
var variables = ref.variables;
var getFragmentSelectionSets = function (doc) { return doc.definitions.filter(isFragmentNode).reduce(function (map, node) {
map[getName(node)] = getSelectionSet(node);
return map;
}, {}); };
/** Returns either the field's name or the field's alias */
return graphqlAnywhere(resolver, query, root, context, variables);
var getFieldAlias = function (node) { return node.alias !== undefined ? node.alias.value : getName(node); };
/** Evaluates a fields arguments taking vars into account */
var getFieldArguments = function (node, vars) {
if (node.arguments === undefined || node.arguments.length === 0) {
return null;
}
return node.arguments.reduce(function (args, arg) {
args[getName(arg)] = evaluateValueNode(arg.value, vars);
return args;
}, {});
};
/** Checks whether a given SelectionNode should be ignored based on @skip or @include directives */
var shouldInclude = function (node, vars) {
if (node.directives === undefined) {
return true;
} // Finds any @include or @skip directive that forces the node to be skipped
return !node.directives.some(function (directive) {
var name = getName(directive); // Ignore other directives
var isInclude = name === 'include';
if (!isInclude && name !== 'skip') {
return false;
} // Get the first argument and expect it to be named "if"
var firstArg = directive.arguments !== undefined ? directive.arguments[0] : null;
if (firstArg === null) {
return false;
} else if (getName(firstArg) !== 'if') {
return false;
}
var value = evaluateValueNode(firstArg.value, vars);
if (typeof value !== 'boolean' && value !== null) {
return false;
} // Return whether this directive forces us to skip
// `@include(if: false)` or `@skip(if: true)`
return isInclude ? !value : !!value;
});
};
var isOperation = function (typeName) { return typeName === 'Query' || typeName === 'Mutation' || typeName === 'Subscription'; };

@@ -49,6 +155,4 @@ var keyOfEntity = function (entity) {

};
var keyForLink = function (parentKey, fieldName, args) {
var key = parentKey + "->" + fieldName;
return args ? (key + "(" + (stringify(args)) + ")") : key;
};
var keyOfField = function (fieldName, args) { return args !== null ? (fieldName + "(" + (stringify(args)) + ")") : fieldName; };
var joinKeys = function (parentKey, key) { return (parentKey + "." + key); };

@@ -63,3 +167,3 @@ var assignObjectToMap = function (map, obj) {

var objectOfMap = function (map) {
var res = Object.create(null);
var res = {};
map.forEach(function (value, key) {

@@ -71,255 +175,378 @@ res[key] = value;

var makeCustomResolver = function (innerResolver) {
return function (fieldName, rootValue, args, context, info) {
var typeName = rootValue.__typename;
var fieldResolver = context.store.getResolver(typeName, fieldName);
/** Creates a new Store with an optional initial, serialized state */
if (fieldResolver !== undefined) {
var result = fieldResolver(args, context, info);
var create = function (initial) {
var records = new Map();
var links = new Map();
if (result !== undefined) {
return result;
}
}
if (initial !== undefined) {
assignObjectToMap(records, initial.records);
assignObjectToMap(links, initial.links);
}
return innerResolver(fieldName, rootValue, args, context, info);
return {
records: records,
links: links
};
};
/** Serializes a given Store to a plain JSON structure */
var entityOfLink = function (store, link) {
if (Array.isArray(link)) {
// @ts-ignore
return link.map(function (inner) { return entityOfLink(store, inner); });
} else if (link === null || typeof link !== 'string') {
return null;
var serialize = function (store) {
var records = objectOfMap(store.records);
var links = objectOfMap(store.links);
return {
records: records,
links: links
};
};
var find = function (store, key) {
var entity = store.records.get(key);
return entity !== undefined ? entity : null;
};
var findOrCreate = function (store, key) {
var entity = find(store, key);
if (entity !== null) {
return entity;
}
return store.getEntity(link);
var record = Object.create(null);
store.records.set(key, record);
return record;
};
var remove = function (store, key) {
store.records.delete(key);
};
var setLink = function (store, key, link) {
store.links.set(key, link);
};
var removeLink = function (store, key) {
store.links.delete(key);
};
var readLink = function (store, key) { return store.links.get(key); };
var queryResolver = function (fieldName, rootValue, args, context, info) {
if (info.isLeaf) {
return rootValue[fieldName];
}
var makeContext = function (store, request) {
var query = request.query;
var variables = request.variables;
var operation = getMainOperation(query);
var store = context.store;
var parentKey = store.keyOfEntity(rootValue);
if (parentKey === null) {
context.isComplete = false;
return null;
if (operation === undefined) {
return;
}
var link = store.getLink(keyForLink(parentKey, fieldName, args));
var dependencies = [];
var fragments = getFragmentSelectionSets(query);
var vars = getNormalizedVars(operation, variables);
var isComplete = true;
return {
dependencies: dependencies,
isComplete: isComplete,
operation: operation,
fragments: fragments,
vars: vars,
store: store
};
};
var forEachFieldNode = function (ctx, select, cb) {
var vars = ctx.vars;
var fragments = ctx.fragments;
select.forEach(function (node) {
if (!shouldInclude(node, vars)) {
// Directives instruct this node to be skipped
return;
} else if (!isFieldNode(node)) {
// This is a fragment (either inline or spread)
var fragmentSelect = isInlineFragment(node) ? getSelectionSet(node) : fragments[getName(node)]; // Recursively process the fragments' selection sets
if (link === null || link === undefined) {
var fieldValue = rootValue[fieldName];
forEachFieldNode(ctx, fragmentSelect, cb);
} else {
cb(node);
}
});
};
var merge = function (dest, src) {
if (src !== null && typeof src === 'object') {
for (var key in src) {
var srcVal = src[key];
if (fieldValue === undefined) {
context.isComplete = false;
return null;
if (!(key in dest)) {
dest[key] = srcVal;
} else {
merge(dest[key], srcVal);
}
}
return fieldValue;
}
};
var entity = entityOfLink(store, link);
/** Reads a request entirely from the store */
if (entity === null) {
context.isComplete = false;
var query = function (store, request) {
var ctx = makeContext(store, request);
if (ctx === undefined) {
return {
isComplete: false,
dependencies: []
};
}
return entity;
};
var resolver = makeCustomResolver(queryResolver);
var query = function (store, request) {
var context = {
isComplete: true,
store: store,
operation: 'query'
};
var response = graphql(resolver, request, store.getOrCreateEntity('Query'), context);
var select = getSelectionSet(ctx.operation);
var data = readEntity(ctx, 'query', select);
return {
dependencies: store.flushTouched(),
isComplete: context.isComplete,
response: response
data: data,
isComplete: ctx.isComplete,
dependencies: ctx.dependencies
};
};
var isLinkableEntity = function (store, x) {
if (Array.isArray(x)) {
// @ts-ignore
return x.every(function (inner) { return isLinkableEntity(store, inner); });
}
var readEntity = function (ctx, key, select) {
var store = ctx.store;
var entity = find(store, key);
return x === null || typeof x === 'object' && store.keyOfEntity(x) !== null;
}; // Transforms a fieldValue to keys of entities
var linkOfEntity = function (store, x) {
if (Array.isArray(x)) {
// @ts-ignore
return x.map(function (inner) { return linkOfEntity(store, inner); });
} else if (x === null || typeof x !== 'object') {
if (entity === null) {
// Cache Incomplete: A missing entity for a key means it wasn't cached
ctx.isComplete = false;
return null;
} else if (key !== 'query') {
ctx.dependencies.push(key);
}
return store.keyOfEntity(x);
var data = Object.create(null);
readSelection(ctx, entity, key, data, select);
return data;
};
var writeResolver = function (fieldName, rootValue, args, ref, info) {
var store = ref.store;
var readSelection = function (ctx, entity, key, data, select) {
var store = ctx.store;
var vars = ctx.vars;
var fragments = ctx.fragments;
select.forEach(function (node) {
if (!shouldInclude(node, vars)) {
// Directives instruct this node to be skipped
return;
} else if (!isFieldNode(node)) {
// This is a fragment (either inline or spread)
var fragmentSelect = isInlineFragment(node) ? getSelectionSet(node) : fragments[getName(node)]; // Recursively process the fragments' selection sets
var fieldValue = rootValue[info.resultKey || fieldName];
var parentKey = store.keyOfEntity(rootValue);
var fragmentData = Object.create(null);
readSelection(ctx, entity, key, fragmentData, fragmentSelect);
merge(data, fragmentData);
} else {
var fieldName = getName(node); // The field's key can include arguments if it has any
if (parentKey === null) {
return null;
} else if (parentKey === 'Mutation' || parentKey === 'Subscription') {
// We do traverse but we don't store Mutation & Subscription themselves
return fieldValue;
} else if (fieldValue === null || fieldValue === undefined) {
// Clear stored field since value is null
store.writeEntityValue(parentKey, fieldName, null);
return null;
} else if (info.isLeaf || typeof fieldValue !== 'object') {
// Write leaf / scalar value to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
return null;
} // Determine if this is a link and not a scalar
var fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
var fieldValue = entity[fieldKey];
var fieldAlias = getFieldAlias(node);
var childFieldKey = joinKeys(key, fieldKey);
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
var shouldCreateLink = isLinkableEntity(store, fieldValue);
if (node.selectionSet === undefined || fieldValue !== null) {
// Cache Incomplete: An undefined field value means it wasn't cached
ctx.isComplete = fieldValue !== undefined;
data[fieldAlias] = fieldValue === undefined ? null : fieldValue;
} else {
// null values mean that a field might be linked to other entities
var ref = node.selectionSet;
var fieldSelect = ref.selections;
var link = readLink(store, childFieldKey); // Cache Incomplete: A missing link for a field means it's not cached
if (!shouldCreateLink) {
// Write object-like scalar to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
if (link === undefined) {
ctx.isComplete = false;
data[fieldAlias] = null;
} else {
data[fieldAlias] = readField(ctx, link, fieldSelect);
}
}
}
});
};
var readField = function (ctx, link, select) {
if (Array.isArray(link)) {
// @ts-ignore: Link cannot be expressed as a recursive type
return link.map(function (childLink) { return readField(ctx, childLink, select); });
} else if (link === null) {
return null;
} // Clear stored field since it's not a scalar
}
return readEntity(ctx, link, select);
};
store.writeEntityValue(parentKey, fieldName, undefined); // Write link to store and keep traversing
/** Writes a request given its response to the store */
var link = linkOfEntity(store, fieldValue);
store.writeLink(keyForLink(parentKey, fieldName, args), link);
return fieldValue;
};
var write = function (store, request, data) {
var ctx = makeContext(store, request);
var resolver$1 = makeCustomResolver(writeResolver);
if (ctx === undefined) {
return {
isComplete: false,
dependencies: []
};
}
var write = function (store, request, response) {
graphql(resolver$1, request, response, {
isComplete: true,
store: store,
operation: 'write'
});
var dependencies = store.flushTouched();
var operation = ctx.operation;
var select = getSelectionSet(operation);
var operationName = operation.operation;
if (operationName === 'subscription' || operationName === 'mutation') {
writeRoot(ctx, data, select);
} else {
writeEntity(ctx, operationName, data, select);
}
return {
isComplete: true,
dependencies: dependencies
dependencies: ctx.dependencies
};
};
var Store = function Store(ref) {
var initial = ref.initial;
var keyExtractor = ref.keyExtractor;
var resolvers = ref.resolvers;
var writeEntity = function (ctx, key, data, select) {
var store = ctx.store;
var entity = findOrCreate(store, key);
this.touched = [];
this.records = new Map();
this.links = new Map();
this.resolvers = resolvers || {};
if (keyExtractor !== undefined) {
this.keyOfEntity = function (entity) {
var key = keyExtractor(entity);
return key !== undefined ? key : keyOfEntity(entity);
};
} else {
this.keyOfEntity = keyOfEntity;
if (key !== 'query') {
ctx.dependencies.push(key);
}
if (initial !== undefined) {
assignObjectToMap(this.records, initial.records);
assignObjectToMap(this.links, initial.links);
}
writeSelection(ctx, entity, key, data, select);
};
Store.prototype.getResolver = function getResolver (typeName, fieldName) {
if (typeName === null || typeName === undefined) {
return undefined;
}
var writeSelection = function (ctx, entity, key, data, select) {
forEachFieldNode(ctx, select, function (node) {
var store = ctx.store;
var vars = ctx.vars;
var fieldName = getName(node);
var fieldValue = data[getFieldAlias(node)]; // The field's key can include arguments if it has any
var typeResolvers = this.resolvers[typeName];
return typeResolvers !== undefined ? typeResolvers[fieldName] : undefined;
};
var fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
var childFieldKey = joinKeys(key, fieldKey);
Store.prototype.getEntity = function getEntity (key) {
if (!isOperation(key)) {
this.touched.push(key);
}
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
var entity = this.records.get(key);
return entity !== undefined ? entity : null;
if (node.selectionSet === undefined || fieldValue === null || typeof fieldValue !== 'object') {
// This is a leaf node, so we're setting the field's value directly
entity[fieldKey] = fieldValue; // Remove any links that might've existed before for this field
removeLink(store, childFieldKey);
} else {
// Ensure that this key exists on the entity and that previous values are thrown away
entity[fieldKey] = null; // Process the field and write links for the child entities that have been written
var ref = node.selectionSet;
var fieldSelect = ref.selections;
var link = writeField(ctx, childFieldKey, fieldValue, fieldSelect);
setLink(store, childFieldKey, link);
}
});
};
Store.prototype.getOrCreateEntity = function getOrCreateEntity (key) {
var prev = this.records.get(key);
var writeField = function (ctx, parentFieldKey, data, select) {
if (Array.isArray(data)) {
return data.map(function (item, index) {
// Append the current index to the parentFieldKey fallback
var indexKey = joinKeys(parentFieldKey, ("" + index)); // Recursively write array data
if (prev !== undefined && prev !== null) {
return prev;
}
var links = writeField(ctx, indexKey, item, select); // Link cannot be expressed as a recursive type
var entity = Object.create(null);
this.records.set(key, entity);
return entity;
};
return links;
});
} else if (data === null) {
return null;
} // Write entity to key that falls back to the given parentFieldKey
Store.prototype.writeEntityValue = function writeEntityValue (key, prop, val) {
if (!isOperation(key)) {
this.touched.push(key);
}
var entity = this.getOrCreateEntity(key);
var entityKey = keyOfEntity(data);
var key = entityKey !== null ? entityKey : parentFieldKey;
writeEntity(ctx, key, data, select);
return key;
}; // This is like writeSelection but assumes no parent entity exists
if (val === undefined) {
delete entity[prop];
} else {
entity[prop] = val;
var writeRoot = function (ctx, data, select) {
forEachFieldNode(ctx, select, function (node) {
var fieldValue = data[getFieldAlias(node)];
if (node.selectionSet !== undefined && typeof fieldValue === 'object') {
var ref = node.selectionSet;
var fieldSelect = ref.selections;
writeRootField(ctx, fieldValue, fieldSelect);
}
});
}; // This is like wroteField but doesn't fall back to a generated key
var writeRootField = function (ctx, data, select) {
if (Array.isArray(data)) {
return data.map(function (item) { return writeRootField(ctx, item, select); });
} else if (data === null) {
return;
} // Write entity to key that falls back to the given parentFieldKey
var entityKey = keyOfEntity(data);
if (entityKey !== null) {
writeEntity(ctx, entityKey, data, select);
}
};
Store.prototype.getLink = function getLink (key) {
this.touched.push(key);
var link = this.links.get(key);
return link !== undefined ? link : null;
var gc = function (store) {
var visitedLinks = new Set();
var visitedRecords = new Set();
var ctx = {
store: store,
visitedLinks: visitedLinks,
visitedRecords: visitedRecords
};
walkEntity(ctx, 'query');
store.records.forEach(function (_entity, key) {
if (!visitedRecords.has(key)) {
remove(store, key);
}
});
store.links.forEach(function (_link, key) {
if (!visitedLinks.has(key)) {
removeLink(store, key);
}
});
};
Store.prototype.writeLink = function writeLink (key, link) {
this.touched.push(key);
var walkEntity = function (ctx, key) {
var store = ctx.store;
var visitedRecords = ctx.visitedRecords;
var visitedLinks = ctx.visitedLinks;
var entity = find(store, key);
if (link === null) {
this.links.delete(key);
} else {
this.links.set(key, link);
if (entity !== null && !visitedRecords.has(key)) {
visitedRecords.add(key);
for (var fieldKey in entity) {
var value = entity[key];
if (value === null) {
var childFieldKey = joinKeys(key, fieldKey);
var link = readLink(store, childFieldKey);
if (link !== undefined && !visitedLinks.has(childFieldKey)) {
visitedLinks.add(childFieldKey);
walkLink(ctx, link);
}
}
}
}
};
Store.prototype.toJSON = function toJSON () {
return {
records: objectOfMap(this.records),
links: objectOfMap(this.links)
};
var walkLink = function (ctx, link) {
if (Array.isArray(link)) {
link.forEach(function (childLink) { return walkLink(ctx, childLink); });
} else if (link !== null) {
walkEntity(ctx, link);
}
};
Store.prototype.flushTouched = function flushTouched () {
var touched = this.touched.filter(function (key, i, arr) {
return arr.indexOf(key) === i;
});
this.touched = [];
return touched;
};
var addTypeNames = function (op) { return (Object.assign({}, op,

@@ -344,5 +571,5 @@ {query: urql.formatDocument(op.query)})); }; // Retrieves the requestPolicy from an operation

var toNetworkOnly = function (operation) { return (Object.assign({}, operation,
var toRequestPolicy = function (operation, requestPolicy) { return (Object.assign({}, operation,
{context: Object.assign({}, operation.context,
{requestPolicy: 'network-only'})})); };
{requestPolicy: requestPolicy})})); };

@@ -353,3 +580,3 @@ var cacheExchange = function (opts) { return function (ref) {

var store = new Store(opts);
var store = create(opts.initial);
var ops = new Map();

@@ -388,3 +615,4 @@ var deps = Object.create(null); // This accepts an array of dependencies and reexecutes all known operations

if (!ops.has(op.key)) {
ops.set(op.key, op);
var isNetworkOnly = op.context.requestPolicy === 'network-only';
ops.set(op.key, isNetworkOnly ? toRequestPolicy(op, 'cache-and-network') : op);
}

@@ -408,3 +636,3 @@ });

isComplete: isComplete,
data: res.response
data: res.data
};

@@ -445,3 +673,3 @@ }; // Take any OperationResult and update the cache with it

if (policy === 'cache-and-network') {
var networkOnly = toNetworkOnly(operation);
var networkOnly = toRequestPolicy(operation, 'network-only');
client.reexecuteOperation(networkOnly);

@@ -457,4 +685,8 @@ }

exports.create = create;
exports.serialize = serialize;
exports.cacheExchange = cacheExchange;
exports.getEntity = getEntity;
exports.query = query;
exports.write = write;
exports.gc = gc;
//# sourceMappingURL=graphcache.js.map
import { Exchange } from 'urql';
import { StoreOpts } from './store';
export declare const cacheExchange: (opts: StoreOpts) => Exchange;
import { SerializedStore } from './store';
export interface CacheExchangeOpts {
initial?: SerializedStore;
}
export declare const cacheExchange: (opts: CacheExchangeOpts) => Exchange;

@@ -0,4 +1,4 @@

export * from './operations';
export * from './types';
export { StoreData } from './store';
export * from './resolvers';
export { create, serialize } from './store';
export { cacheExchange } from './exchange';

@@ -1,2 +0,3 @@

export { default as query } from './query';
export { default as write } from './write';
export { query } from './query';
export { write } from './write';
export { gc } from './gc';

@@ -1,4 +0,4 @@

import Store from '../store';
import { Request, Result } from '../types';
declare const query: (store: Store, request: Request) => Result;
export default query;
import { Store } from '../store';
import { Request, Result } from './types';
/** Reads a request entirely from the store */
export declare const query: (store: Store, request: Request) => Result;

@@ -1,4 +0,7 @@

import Store from '../store';
import { Entity, Request, Result } from '../types';
declare const write: (store: Store, request: Request, response: Entity) => Result;
export default write;
import { Store } from '../store';
import { Data, Request, Result } from './types';
export interface WriteResult {
touched: string[];
}
/** Writes a request given its response to the store */
export declare const write: (store: Store, request: Request, data: Data) => Result;

@@ -1,40 +0,22 @@

import { DocumentNode } from 'graphql';
import { ExecInfo } from 'graphql-anywhere';
import Store from './store';
export interface Request {
query: DocumentNode;
variables?: object;
}
export declare type Scalar = string | number | null;
/** A scalar is any fieldValue without a type. It can also include lists of scalars and embedded objects, which are simply represented as empty object type. */
export declare type Scalar = {} | string | number | null;
export declare type NullPrototype = {
[K in keyof ObjectConstructor]: never;
};
export interface SystemFields {
__typename?: string | null;
_id?: Scalar;
id?: Scalar;
__typename?: string;
_id?: string | number | null;
id?: string | number | null;
}
export declare type KeyExtractor = (entity: Entity) => void | null | string;
export declare type FieldValue = Entity | Scalar | Array<Entity | Scalar>;
export interface EntityFields {
[property: string]: FieldValue;
[fieldName: string]: Scalar;
}
export declare type Entity = SystemFields & EntityFields;
/** Every Entity must have a typeName. It might have some ID fields of which `id` and `_id` are recognised by default. Every other fieldValue is a scalar. */
export declare type Entity = NullPrototype & SystemFields & EntityFields;
/** A link is a key or array of keys referencing other entities in the Records Map. It may be or contain `null`. */
export declare type Link = null | string | Array<string | null>;
export declare type EntityMap = Map<string, Entity>;
export declare type LinkMap = Map<string, Link>;
export declare type CacheOperation = 'query' | 'write';
export interface Context {
store: Store;
operation: CacheOperation;
isComplete: boolean;
}
export interface Result {
dependencies: string[];
isComplete: boolean;
response?: Entity;
}
export declare type FieldResolver = (fieldName: string, rootValue: Entity, args: null | object, context: Context, info: ExecInfo) => FieldValue;
export declare type CacheResolver = (args: null | object, context: Context, info: ExecInfo) => FieldValue | void;
export interface CacheResolvers {
[typeName: string]: {
[fieldName: string]: CacheResolver;
};
}
/** A link can be resolved into the entities it points to. The resulting structure is a ResolvedLink */
export declare type ResolvedLink = null | Entity | Array<Entity | null>;
export declare type Records = Map<string, Entity>;
export declare type Links = Map<string, Link>;
export declare type Embedded = Map<string, Scalar>;
{
"name": "@urql/exchange-graphcache",
"version": "0.1.0-alpha.2",
"version": "0.1.0-alpha.3",
"description": "A normalizing GraphQL cache configurable by defining exceptions to the rule",

@@ -77,3 +77,2 @@ "main": "dist/graphcache.js",

"fast-json-stable-stringify": "^2.0.0",
"graphql-anywhere": "^4.1.28",
"wonka": "^2.0.1"

@@ -80,0 +79,0 @@ },

@@ -1,6 +0,12 @@

import { Exchange, formatDocument, Operation, OperationResult } from 'urql';
import {
Exchange,
formatDocument,
Operation,
OperationResult,
RequestPolicy,
} from 'urql';
import { filter, map, merge, pipe, share, tap } from 'wonka';
import { query, write } from './operations';
import Store, { StoreOpts } from './store';
import { create, SerializedStore } from './store';

@@ -49,15 +55,22 @@ type OperationResultWithMeta = OperationResult & {

// Copy an operation and change the requestPolicy to skip the cache
const toNetworkOnly = (operation: Operation): Operation => ({
const toRequestPolicy = (
operation: Operation,
requestPolicy: RequestPolicy
): Operation => ({
...operation,
context: {
...operation.context,
requestPolicy: 'network-only',
requestPolicy,
},
});
export const cacheExchange = (opts: StoreOpts): Exchange => ({
export interface CacheExchangeOpts {
initial?: SerializedStore;
}
export const cacheExchange = (opts: CacheExchangeOpts): Exchange => ({
forward,
client,
}) => {
const store = new Store(opts);
const store = create(opts.initial);
const ops: OperationMap = new Map();

@@ -101,3 +114,7 @@ const deps = Object.create(null) as DependentOperations;

if (!ops.has(op.key)) {
ops.set(op.key, op);
const isNetworkOnly = op.context.requestPolicy === 'network-only';
ops.set(
op.key,
isNetworkOnly ? toRequestPolicy(op, 'cache-and-network') : op
);
}

@@ -122,3 +139,3 @@ });

isComplete,
data: res.response,
data: res.data,
};

@@ -179,3 +196,3 @@ };

if (policy === 'cache-and-network') {
const networkOnly = toNetworkOnly(operation);
const networkOnly = toRequestPolicy(operation, 'network-only');
client.reexecuteOperation(networkOnly);

@@ -182,0 +199,0 @@ }

@@ -0,4 +1,5 @@

export * from './operations';
export * from './types';
export { StoreData } from './store';
export * from './resolvers';
export { create, serialize } from './store';
export { cacheExchange } from './exchange';

@@ -1,2 +0,3 @@

export { default as query } from './query';
export { default as write } from './write';
export { query } from './query';
export { write } from './write';
export { gc } from './gc';

@@ -1,73 +0,124 @@

import Store from '../store';
import { Context, FieldResolver, Link, Request, Result } from '../types';
import { graphql, keyForLink } from '../utils';
import { makeCustomResolver } from './custom';
import {
getFieldAlias,
getFieldArguments,
getName,
getSelectionSet,
isFieldNode,
isInlineFragment,
SelectionSet,
shouldInclude,
} from '../ast';
const entityOfLink = (store: Store, link: Link) => {
if (Array.isArray(link)) {
// @ts-ignore
return link.map(inner => entityOfLink(store, inner));
} else if (link === null || typeof link !== 'string') {
return null;
}
import { joinKeys, keyOfField } from '../helpers';
import { find, readLink, Store } from '../store';
import { Entity, Link } from '../types';
return store.getEntity(link);
};
import { makeContext, merge } from './shared';
import { Context, Data, Request, Result } from './types';
const queryResolver: FieldResolver = (
fieldName,
rootValue,
args,
context,
info
) => {
if (info.isLeaf) {
return rootValue[fieldName];
/** Reads a request entirely from the store */
export const query = (store: Store, request: Request): Result => {
const ctx = makeContext(store, request);
if (ctx === undefined) {
return { isComplete: false, dependencies: [] };
}
const { store } = context;
const parentKey = store.keyOfEntity(rootValue);
if (parentKey === null) {
context.isComplete = false;
return null;
}
const select = getSelectionSet(ctx.operation);
const data = readEntity(ctx, 'query', select);
const link = store.getLink(keyForLink(parentKey, fieldName, args));
if (link === null || link === undefined) {
const fieldValue = rootValue[fieldName];
if (fieldValue === undefined) {
context.isComplete = false;
return null;
}
return {
data,
isComplete: ctx.isComplete,
dependencies: ctx.dependencies,
};
};
return fieldValue;
}
const entity = entityOfLink(store, link);
const readEntity = (
ctx: Context,
key: string,
select: SelectionSet
): Data | null => {
const { store } = ctx;
const entity = find(store, key);
if (entity === null) {
context.isComplete = false;
// Cache Incomplete: A missing entity for a key means it wasn't cached
ctx.isComplete = false;
return null;
} else if (key !== 'query') {
ctx.dependencies.push(key);
}
return entity;
const data = Object.create(null);
readSelection(ctx, entity, key, data, select);
return data;
};
const resolver = makeCustomResolver(queryResolver);
const readSelection = (
ctx: Context,
entity: Entity,
key: string,
data: Data,
select: SelectionSet
): void => {
const { store, vars, fragments } = ctx;
const query = (store: Store, request: Request): Result => {
const context: Context = { isComplete: true, store, operation: 'query' };
select.forEach(node => {
if (!shouldInclude(node, vars)) {
// Directives instruct this node to be skipped
return;
} else if (!isFieldNode(node)) {
// This is a fragment (either inline or spread)
const fragmentSelect = isInlineFragment(node)
? getSelectionSet(node)
: fragments[getName(node)];
const response = graphql(
resolver,
request,
store.getOrCreateEntity('Query'),
context
);
// Recursively process the fragments' selection sets
const fragmentData = Object.create(null);
readSelection(ctx, entity, key, fragmentData, fragmentSelect);
merge(data, fragmentData);
} else {
const fieldName = getName(node);
// The field's key can include arguments if it has any
const fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
const fieldValue = entity[fieldKey];
const fieldAlias = getFieldAlias(node);
const childFieldKey = joinKeys(key, fieldKey);
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
return {
dependencies: store.flushTouched(),
isComplete: context.isComplete,
response,
};
if (node.selectionSet === undefined || fieldValue !== null) {
// Cache Incomplete: An undefined field value means it wasn't cached
ctx.isComplete = fieldValue !== undefined;
data[fieldAlias] = fieldValue === undefined ? null : fieldValue;
} else {
// null values mean that a field might be linked to other entities
const { selections: fieldSelect } = node.selectionSet;
const link = readLink(store, childFieldKey);
// Cache Incomplete: A missing link for a field means it's not cached
if (link === undefined) {
ctx.isComplete = false;
data[fieldAlias] = null;
} else {
data[fieldAlias] = readField(ctx, link, fieldSelect);
}
}
}
});
};
export default query;
const readField = (
ctx: Context,
link: Link,
select: SelectionSet
): null | Data | Data[] => {
if (Array.isArray(link)) {
// @ts-ignore: Link cannot be expressed as a recursive type
return link.map(childLink => readField(ctx, childLink, select));
} else if (link === null) {
return null;
}
return readEntity(ctx, link, select);
};

@@ -1,91 +0,147 @@

import Store from '../store';
import { graphql, keyForLink } from '../utils';
import { makeCustomResolver } from './custom';
import {
Entity,
FieldResolver,
FieldValue,
Link,
Request,
Result,
} from '../types';
getFieldAlias,
getFieldArguments,
getName,
getSelectionSet,
SelectionSet,
} from '../ast';
// Determines whether a fieldValue consists of only entities
const isLinkableEntity = (store: Store, x: FieldValue) => {
if (Array.isArray(x)) {
// @ts-ignore
return x.every(inner => isLinkableEntity(store, inner));
import { joinKeys, keyOfEntity, keyOfField } from '../helpers';
import { findOrCreate, removeLink, setLink, Store } from '../store';
import { Entity, Link } from '../types';
import { forEachFieldNode, makeContext } from './shared';
import { Context, Data, Request, Result } from './types';
export interface WriteResult {
touched: string[];
}
/** Writes a request given its response to the store */
export const write = (store: Store, request: Request, data: Data): Result => {
const ctx = makeContext(store, request);
if (ctx === undefined) {
return { isComplete: false, dependencies: [] };
}
return x === null || (typeof x === 'object' && store.keyOfEntity(x) !== null);
const { operation } = ctx;
const select = getSelectionSet(operation);
const { operation: operationName } = operation;
if (operationName === 'subscription' || operationName === 'mutation') {
writeRoot(ctx, data, select);
} else {
writeEntity(ctx, operationName, data, select);
}
return { isComplete: true, dependencies: ctx.dependencies };
};
// Transforms a fieldValue to keys of entities
const linkOfEntity = (store: Store, x: FieldValue): Link => {
if (Array.isArray(x)) {
// @ts-ignore
return x.map(inner => linkOfEntity(store, inner));
} else if (x === null || typeof x !== 'object') {
return null;
const writeEntity = (
ctx: Context,
key: string,
data: Data,
select: SelectionSet
) => {
const { store } = ctx;
const entity = findOrCreate(store, key);
if (key !== 'query') {
ctx.dependencies.push(key);
}
return store.keyOfEntity(x);
writeSelection(ctx, entity, key, data, select);
};
const writeResolver: FieldResolver = (
fieldName,
rootValue,
args,
{ store },
info
const writeSelection = (
ctx: Context,
entity: Entity,
key: string,
data: Data,
select: SelectionSet
) => {
const fieldValue = rootValue[info.resultKey || fieldName];
const parentKey = store.keyOfEntity(rootValue);
forEachFieldNode(ctx, select, node => {
const { store, vars } = ctx;
const fieldName = getName(node);
const fieldValue = data[getFieldAlias(node)];
// The field's key can include arguments if it has any
const fieldKey = keyOfField(fieldName, getFieldArguments(node, vars));
const childFieldKey = joinKeys(key, fieldKey);
if (key === 'query') {
ctx.dependencies.push(childFieldKey);
}
if (parentKey === null) {
return null;
} else if (parentKey === 'Mutation' || parentKey === 'Subscription') {
// We do traverse but we don't store Mutation & Subscription themselves
return fieldValue;
} else if (fieldValue === null || fieldValue === undefined) {
// Clear stored field since value is null
store.writeEntityValue(parentKey, fieldName, null);
return null;
} else if (info.isLeaf || typeof fieldValue !== 'object') {
// Write leaf / scalar value to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
return null;
}
if (
node.selectionSet === undefined ||
fieldValue === null ||
typeof fieldValue !== 'object'
) {
// This is a leaf node, so we're setting the field's value directly
entity[fieldKey] = fieldValue;
// Remove any links that might've existed before for this field
removeLink(store, childFieldKey);
} else {
// Ensure that this key exists on the entity and that previous values are thrown away
entity[fieldKey] = null;
// Determine if this is a link and not a scalar
const shouldCreateLink = isLinkableEntity(store, fieldValue);
if (!shouldCreateLink) {
// Write object-like scalar to parent
store.writeEntityValue(parentKey, fieldName, fieldValue);
// Process the field and write links for the child entities that have been written
const { selections: fieldSelect } = node.selectionSet;
const link = writeField(ctx, childFieldKey, fieldValue, fieldSelect);
setLink(store, childFieldKey, link);
}
});
};
const writeField = (
ctx: Context,
parentFieldKey: string,
data: Data | Data[] | null,
select: SelectionSet
): Link => {
if (Array.isArray(data)) {
return data.map((item, index) => {
// Append the current index to the parentFieldKey fallback
const indexKey = joinKeys(parentFieldKey, `${index}`);
// Recursively write array data
const links = writeField(ctx, indexKey, item, select);
// Link cannot be expressed as a recursive type
return links as string | null;
});
} else if (data === null) {
return null;
}
// Clear stored field since it's not a scalar
store.writeEntityValue(parentKey, fieldName, undefined);
// Write entity to key that falls back to the given parentFieldKey
const entityKey = keyOfEntity(data);
const key = entityKey !== null ? entityKey : parentFieldKey;
writeEntity(ctx, key, data, select);
return key;
};
// Write link to store and keep traversing
const link = linkOfEntity(store, fieldValue);
store.writeLink(keyForLink(parentKey, fieldName, args), link);
return fieldValue;
// This is like writeSelection but assumes no parent entity exists
const writeRoot = (ctx: Context, data: Data, select: SelectionSet) => {
forEachFieldNode(ctx, select, node => {
const fieldValue = data[getFieldAlias(node)];
if (node.selectionSet !== undefined && typeof fieldValue === 'object') {
const { selections: fieldSelect } = node.selectionSet;
writeRootField(ctx, fieldValue, fieldSelect);
}
});
};
const resolver = makeCustomResolver(writeResolver);
// This is like wroteField but doesn't fall back to a generated key
const writeRootField = (
ctx: Context,
data: Data | Data[] | null,
select: SelectionSet
) => {
if (Array.isArray(data)) {
return data.map(item => writeRootField(ctx, item, select));
} else if (data === null) {
return;
}
const write = (store: Store, request: Request, response: Entity): Result => {
graphql(resolver, request, response, {
isComplete: true,
store,
operation: 'write',
});
const dependencies = store.flushTouched();
return { isComplete: true, dependencies };
// Write entity to key that falls back to the given parentFieldKey
const entityKey = keyOfEntity(data);
if (entityKey !== null) {
writeEntity(ctx, entityKey, data, select);
}
};
export default write;

@@ -1,63 +0,27 @@

import { DocumentNode } from 'graphql';
import { ExecInfo } from 'graphql-anywhere';
import Store from './store';
/** A scalar is any fieldValue without a type. It can also include lists of scalars and embedded objects, which are simply represented as empty object type. */
export type Scalar = {} | string | number | null;
export interface Request {
query: DocumentNode;
variables?: object;
}
export type NullPrototype = { [K in keyof ObjectConstructor]: never };
export type Scalar = string | number | null;
export interface SystemFields {
__typename?: string | null;
_id?: Scalar;
id?: Scalar;
__typename?: string;
_id?: string | number | null;
id?: string | number | null;
}
export type KeyExtractor = (entity: Entity) => void | null | string;
export type FieldValue = Entity | Scalar | Array<Entity | Scalar>;
export interface EntityFields {
[property: string]: FieldValue;
[fieldName: string]: Scalar;
}
export type Entity = SystemFields & EntityFields;
/** Every Entity must have a typeName. It might have some ID fields of which `id` and `_id` are recognised by default. Every other fieldValue is a scalar. */
export type Entity = NullPrototype & SystemFields & EntityFields;
/** A link is a key or array of keys referencing other entities in the Records Map. It may be or contain `null`. */
export type Link = null | string | Array<string | null>;
export type EntityMap = Map<string, Entity>;
export type LinkMap = Map<string, Link>;
export type CacheOperation = 'query' | 'write';
/** A link can be resolved into the entities it points to. The resulting structure is a ResolvedLink */
export type ResolvedLink = null | Entity | Array<Entity | null>;
export interface Context {
store: Store;
operation: CacheOperation;
isComplete: boolean;
}
export interface Result {
dependencies: string[];
isComplete: boolean;
response?: Entity;
}
export type FieldResolver = (
fieldName: string,
rootValue: Entity,
args: null | object,
context: Context,
info: ExecInfo
) => FieldValue;
export type CacheResolver = (
args: null | object,
context: Context,
info: ExecInfo
) => FieldValue | void;
export interface CacheResolvers {
[typeName: string]: {
[fieldName: string]: CacheResolver;
};
}
export type Records = Map<string, Entity>;
export type Links = Map<string, Link>;
export type Embedded = Map<string, Scalar>;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc