relay-runtime
Version:
A core runtime for building GraphQL-driven applications.
425 lines (424 loc) • 18.1 kB
JavaScript
'use strict';
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];
var _createForOfIteratorHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelper"));
var RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator');
var RelayRecordSourceProxy = require('../mutations/RelayRecordSourceProxy');
var getOperation = require('../util/getOperation');
var _require = require('./ClientID'),
isClientID = _require.isClientID;
var cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
var cloneRelayScalarHandleSourceField = require('./cloneRelayScalarHandleSourceField');
var _require2 = require('./RelayConcreteVariables'),
getLocalVariables = _require2.getLocalVariables;
var RelayModernRecord = require('./RelayModernRecord');
var _require3 = require('./RelayRecordState'),
EXISTENT = _require3.EXISTENT,
UNKNOWN = _require3.UNKNOWN;
var RelayStoreUtils = require('./RelayStoreUtils');
var _require4 = require('./TypeID'),
TYPE_SCHEMA_TYPE = _require4.TYPE_SCHEMA_TYPE,
generateTypeID = _require4.generateTypeID;
var invariant = require('invariant');
var getModuleOperationKey = RelayStoreUtils.getModuleOperationKey,
getStorageKey = RelayStoreUtils.getStorageKey,
getArgumentValues = RelayStoreUtils.getArgumentValues;
function check(getSourceForActor, getTargetForActor, defaultActorIdentifier, selector, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers) {
if (log != null) {
log({
name: 'store.datachecker.start',
selector: selector
});
}
var dataID = selector.dataID,
node = selector.node,
variables = selector.variables;
var checker = new DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers);
var result = checker.check(node, dataID);
if (log != null) {
log({
name: 'store.datachecker.end',
selector: selector
});
}
return result;
}
var DataChecker = /*#__PURE__*/function () {
function DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers) {
this._getSourceForActor = getSourceForActor;
this._getTargetForActor = getTargetForActor;
this._getDataID = getDataID;
this._source = getSourceForActor(defaultActorIdentifier);
this._mutatorRecordSourceProxyCache = new Map();
var _this$_getMutatorAndR = this._getMutatorAndRecordProxyForActor(defaultActorIdentifier),
mutator = _this$_getMutatorAndR[0],
recordSourceProxy = _this$_getMutatorAndR[1];
this._useExecTimeResolvers = useExecTimeResolvers !== null && useExecTimeResolvers !== void 0 ? useExecTimeResolvers : false;
this._mostRecentlyInvalidatedAt = null;
this._handlers = handlers;
this._mutator = mutator;
this._operationLoader = operationLoader !== null && operationLoader !== void 0 ? operationLoader : null;
this._recordSourceProxy = recordSourceProxy;
this._recordWasMissing = false;
this._variables = variables;
this._shouldProcessClientComponents = shouldProcessClientComponents;
this._log = log;
}
var _proto = DataChecker.prototype;
_proto._getMutatorAndRecordProxyForActor = function _getMutatorAndRecordProxyForActor(actorIdentifier) {
var tuple = this._mutatorRecordSourceProxyCache.get(actorIdentifier);
if (tuple == null) {
var target = this._getTargetForActor(actorIdentifier);
var mutator = new RelayRecordSourceMutator(this._getSourceForActor(actorIdentifier), target);
var recordSourceProxy = new RelayRecordSourceProxy(mutator, this._getDataID, undefined, this._handlers, this._log);
tuple = [mutator, recordSourceProxy];
this._mutatorRecordSourceProxyCache.set(actorIdentifier, tuple);
}
return tuple;
};
_proto.check = function check(node, dataID) {
this._assignClientAbstractTypes(node);
this._traverse(node, dataID);
return this._recordWasMissing === true ? {
status: 'missing',
mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt
} : {
status: 'available',
mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt
};
};
_proto._getVariableValue = function _getVariableValue(name) {
!this._variables.hasOwnProperty(name) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayAsyncLoader(): Undefined variable `%s`.', name) : invariant(false) : void 0;
return this._variables[name];
};
_proto._handleMissing = function _handleMissing() {
this._recordWasMissing = true;
};
_proto._handleMissingScalarField = function _handleMissingScalarField(field, dataID) {
if (field.name === 'id' && field.alias == null && isClientID(dataID)) {
return undefined;
}
var args = field.args != undefined ? getArgumentValues(field.args, this._variables) : {};
var _iterator = (0, _createForOfIteratorHelper2["default"])(this._handlers),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var handler = _step.value;
if (handler.kind === 'scalar') {
var newValue = handler.handle(field, this._recordSourceProxy.get(dataID), args, this._recordSourceProxy);
if (newValue !== undefined) {
return newValue;
}
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
this._handleMissing();
};
_proto._handleMissingLinkField = function _handleMissingLinkField(field, dataID) {
var args = field.args != undefined ? getArgumentValues(field.args, this._variables) : {};
var _iterator2 = (0, _createForOfIteratorHelper2["default"])(this._handlers),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var handler = _step2.value;
if (handler.kind === 'linked') {
var newValue = handler.handle(field, this._recordSourceProxy.get(dataID), args, this._recordSourceProxy);
if (newValue !== undefined && (newValue === null || this._mutator.getStatus(newValue) === EXISTENT)) {
return newValue;
}
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
this._handleMissing();
};
_proto._handleMissingPluralLinkField = function _handleMissingPluralLinkField(field, dataID) {
var _this = this;
var args = field.args != undefined ? getArgumentValues(field.args, this._variables) : {};
var _iterator3 = (0, _createForOfIteratorHelper2["default"])(this._handlers),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var handler = _step3.value;
if (handler.kind === 'pluralLinked') {
var newValue = handler.handle(field, this._recordSourceProxy.get(dataID), args, this._recordSourceProxy);
if (newValue != null) {
var allItemsKnown = newValue.every(function (linkedID) {
return linkedID != null && _this._mutator.getStatus(linkedID) === EXISTENT;
});
if (allItemsKnown) {
return newValue;
}
} else if (newValue === null) {
return null;
}
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
this._handleMissing();
};
_proto._traverse = function _traverse(node, dataID) {
var status = this._mutator.getStatus(dataID);
if (status === UNKNOWN) {
this._handleMissing();
}
if (status === EXISTENT) {
var record = this._source.get(dataID);
var invalidatedAt = RelayModernRecord.getInvalidationEpoch(record);
if (invalidatedAt != null) {
this._mostRecentlyInvalidatedAt = this._mostRecentlyInvalidatedAt != null ? Math.max(this._mostRecentlyInvalidatedAt, invalidatedAt) : invalidatedAt;
}
this._traverseSelections(node.selections, dataID);
}
};
_proto._traverseSelections = function _traverseSelections(selections, dataID) {
var _this2 = this;
selections.forEach(function (selection) {
switch (selection.kind) {
case 'ScalarField':
_this2._checkScalar(selection, dataID);
break;
case 'LinkedField':
if (selection.plural) {
_this2._checkPluralLink(selection, dataID);
} else {
_this2._checkLink(selection, dataID);
}
break;
case 'ActorChange':
_this2._checkActorChange(selection.linkedField, dataID);
break;
case 'Condition':
var conditionValue = Boolean(_this2._getVariableValue(selection.condition));
if (conditionValue === selection.passingValue) {
_this2._traverseSelections(selection.selections, dataID);
}
break;
case 'InlineFragment':
{
var _abstractKey = selection.abstractKey;
if (_abstractKey == null) {
var typeName = _this2._mutator.getType(dataID);
if (typeName === selection.type) {
_this2._traverseSelections(selection.selections, dataID);
}
} else {
var _recordType = _this2._mutator.getType(dataID);
!(_recordType != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'DataChecker: Expected record `%s` to have a known type', dataID) : invariant(false) : void 0;
var _typeID = generateTypeID(_recordType);
var _implementsInterface = _this2._mutator.getValue(_typeID, _abstractKey);
if (_implementsInterface === true) {
_this2._traverseSelections(selection.selections, dataID);
} else if (_implementsInterface == null) {
_this2._handleMissing();
}
}
break;
}
case 'LinkedHandle':
{
var handleField = cloneRelayHandleSourceField(selection, selections, _this2._variables);
if (handleField.plural) {
_this2._checkPluralLink(handleField, dataID);
} else {
_this2._checkLink(handleField, dataID);
}
break;
}
case 'ScalarHandle':
{
var _handleField = cloneRelayScalarHandleSourceField(selection, selections, _this2._variables);
_this2._checkScalar(_handleField, dataID);
break;
}
case 'ModuleImport':
_this2._checkModuleImport(selection, dataID);
break;
case 'Defer':
case 'Stream':
_this2._traverseSelections(selection.selections, dataID);
break;
case 'FragmentSpread':
var prevVariables = _this2._variables;
_this2._variables = getLocalVariables(_this2._variables, selection.fragment.argumentDefinitions, selection.args);
_this2._traverseSelections(selection.fragment.selections, dataID);
_this2._variables = prevVariables;
break;
case 'ClientExtension':
var recordWasMissing = _this2._recordWasMissing;
_this2._traverseSelections(selection.selections, dataID);
_this2._recordWasMissing = recordWasMissing;
break;
case 'TypeDiscriminator':
var abstractKey = selection.abstractKey;
var recordType = _this2._mutator.getType(dataID);
!(recordType != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'DataChecker: Expected record `%s` to have a known type', dataID) : invariant(false) : void 0;
var typeID = generateTypeID(recordType);
var implementsInterface = _this2._mutator.getValue(typeID, abstractKey);
if (implementsInterface == null) {
_this2._handleMissing();
}
break;
case 'ClientComponent':
if (_this2._shouldProcessClientComponents === false) {
break;
}
_this2._traverseSelections(selection.fragment.selections, dataID);
break;
case 'RelayResolver':
case 'RelayLiveResolver':
if (!_this2._useExecTimeResolvers) {
_this2._checkResolver(selection, dataID);
}
break;
case 'ClientEdgeToClientObject':
if (!_this2._useExecTimeResolvers) {
_this2._checkResolver(selection.backingField, dataID);
}
break;
default:
selection;
!false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayAsyncLoader(): Unexpected ast kind `%s`.', selection.kind) : invariant(false) : void 0;
}
});
};
_proto._checkResolver = function _checkResolver(resolver, dataID) {
if (resolver.fragment) {
this._traverseSelections([resolver.fragment], dataID);
}
};
_proto._checkModuleImport = function _checkModuleImport(moduleImport, dataID) {
var operationLoader = this._operationLoader;
!(operationLoader !== null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'DataChecker: Expected an operationLoader to be configured when using `@module`.') : invariant(false) : void 0;
var operationKey = getModuleOperationKey(moduleImport.documentName);
var operationReference = this._mutator.getValue(dataID, operationKey);
if (operationReference == null) {
if (operationReference === undefined) {
this._handleMissing();
}
return;
}
var normalizationRootNode = operationLoader.get(operationReference);
if (normalizationRootNode != null) {
var operation = getOperation(normalizationRootNode);
var prevVariables = this._variables;
this._variables = getLocalVariables(this._variables, operation.argumentDefinitions, moduleImport.args);
this._traverse(operation, dataID);
this._variables = prevVariables;
} else {
this._handleMissing();
}
};
_proto._checkScalar = function _checkScalar(field, dataID) {
var storageKey = getStorageKey(field, this._variables);
var fieldValue = this._mutator.getValue(dataID, storageKey);
if (fieldValue === undefined) {
fieldValue = this._handleMissingScalarField(field, dataID);
if (fieldValue !== undefined) {
this._mutator.setValue(dataID, storageKey, fieldValue);
}
}
};
_proto._checkLink = function _checkLink(field, dataID) {
var storageKey = getStorageKey(field, this._variables);
var linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
if (linkedID === undefined) {
linkedID = this._handleMissingLinkField(field, dataID);
if (linkedID != null) {
this._mutator.setLinkedRecordID(dataID, storageKey, linkedID);
} else if (linkedID === null) {
this._mutator.setValue(dataID, storageKey, null);
}
}
if (linkedID != null) {
this._traverse(field, linkedID);
}
};
_proto._checkPluralLink = function _checkPluralLink(field, dataID) {
var _this3 = this;
var storageKey = getStorageKey(field, this._variables);
var linkedIDs = this._mutator.getLinkedRecordIDs(dataID, storageKey);
if (linkedIDs === undefined) {
linkedIDs = this._handleMissingPluralLinkField(field, dataID);
if (linkedIDs != null) {
this._mutator.setLinkedRecordIDs(dataID, storageKey, linkedIDs);
} else if (linkedIDs === null) {
this._mutator.setValue(dataID, storageKey, null);
}
}
if (linkedIDs) {
linkedIDs.forEach(function (linkedID) {
if (linkedID != null) {
_this3._traverse(field, linkedID);
}
});
}
};
_proto._checkActorChange = function _checkActorChange(field, dataID) {
var storageKey = getStorageKey(field, this._variables);
var record = this._source.get(dataID);
var tuple = record != null ? RelayModernRecord.getActorLinkedRecordID(record, storageKey) : record;
if (tuple == null) {
if (tuple === undefined) {
this._handleMissing();
}
} else {
var actorIdentifier = tuple[0],
linkedID = tuple[1];
var prevSource = this._source;
var prevMutator = this._mutator;
var prevRecordSourceProxy = this._recordSourceProxy;
var _this$_getMutatorAndR2 = this._getMutatorAndRecordProxyForActor(actorIdentifier),
mutator = _this$_getMutatorAndR2[0],
recordSourceProxy = _this$_getMutatorAndR2[1];
this._source = this._getSourceForActor(actorIdentifier);
this._mutator = mutator;
this._recordSourceProxy = recordSourceProxy;
this._assignClientAbstractTypes(field);
this._traverse(field, linkedID);
this._source = prevSource;
this._mutator = prevMutator;
this._recordSourceProxy = prevRecordSourceProxy;
}
};
_proto._assignClientAbstractTypes = function _assignClientAbstractTypes(node) {
var clientAbstractTypes = node.clientAbstractTypes;
if (clientAbstractTypes != null) {
for (var _i = 0, _Object$keys = Object.keys(clientAbstractTypes); _i < _Object$keys.length; _i++) {
var abstractType = _Object$keys[_i];
var _iterator4 = (0, _createForOfIteratorHelper2["default"])(clientAbstractTypes[abstractType]),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var concreteType = _step4.value;
var typeID = generateTypeID(concreteType);
if (this._source.get(typeID) == null) {
this._mutator.create(typeID, TYPE_SCHEMA_TYPE);
}
if (this._mutator.getValue(typeID, abstractType) == null) {
this._mutator.setValue(typeID, abstractType, true);
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
}
}
};
return DataChecker;
}();
module.exports = {
check: check
};