@urql/exchange-graphcache
Advanced tools
Comparing version 0.1.0-alpha.2 to 0.1.0-alpha.3
@@ -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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
192576
4
42
2107
1
- Removedgraphql-anywhere@^4.1.28
- Removed@wry/equality@0.1.11(transitive)
- Removedapollo-utilities@1.3.4(transitive)
- Removedgraphql-anywhere@4.2.8(transitive)
- Removedts-invariant@0.3.30.4.4(transitive)
- Removedtslib@1.14.12.6.3(transitive)