@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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
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.8.1(transitive)