relay-runtime 19.0.0 → 20.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/experimental.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v19.0.0
2
+ * Relay v20.1.0
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v19.0.0
2
+ * Relay v20.1.0
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
@@ -23,7 +23,7 @@ var invariant = require('invariant');
23
23
  var getModuleOperationKey = RelayStoreUtils.getModuleOperationKey,
24
24
  getStorageKey = RelayStoreUtils.getStorageKey,
25
25
  getArgumentValues = RelayStoreUtils.getArgumentValues;
26
- function check(getSourceForActor, getTargetForActor, defaultActorIdentifier, selector, handlers, operationLoader, getDataID, shouldProcessClientComponents, log) {
26
+ function check(getSourceForActor, getTargetForActor, defaultActorIdentifier, selector, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers) {
27
27
  if (log != null) {
28
28
  log({
29
29
  name: 'store.datachecker.start',
@@ -33,7 +33,7 @@ function check(getSourceForActor, getTargetForActor, defaultActorIdentifier, sel
33
33
  var dataID = selector.dataID,
34
34
  node = selector.node,
35
35
  variables = selector.variables;
36
- var checker = new DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log);
36
+ var checker = new DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers);
37
37
  var result = checker.check(node, dataID);
38
38
  if (log != null) {
39
39
  log({
@@ -44,7 +44,7 @@ function check(getSourceForActor, getTargetForActor, defaultActorIdentifier, sel
44
44
  return result;
45
45
  }
46
46
  var DataChecker = /*#__PURE__*/function () {
47
- function DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log) {
47
+ function DataChecker(getSourceForActor, getTargetForActor, defaultActorIdentifier, variables, handlers, operationLoader, getDataID, shouldProcessClientComponents, log, useExecTimeResolvers) {
48
48
  this._getSourceForActor = getSourceForActor;
49
49
  this._getTargetForActor = getTargetForActor;
50
50
  this._getDataID = getDataID;
@@ -53,6 +53,7 @@ var DataChecker = /*#__PURE__*/function () {
53
53
  var _this$_getMutatorAndR = this._getMutatorAndRecordProxyForActor(defaultActorIdentifier),
54
54
  mutator = _this$_getMutatorAndR[0],
55
55
  recordSourceProxy = _this$_getMutatorAndR[1];
56
+ this._useExecTimeResolvers = useExecTimeResolvers !== null && useExecTimeResolvers !== void 0 ? useExecTimeResolvers : false;
56
57
  this._mostRecentlyInvalidatedAt = null;
57
58
  this._handlers = handlers;
58
59
  this._mutator = mutator;
@@ -277,10 +278,14 @@ var DataChecker = /*#__PURE__*/function () {
277
278
  break;
278
279
  case 'RelayResolver':
279
280
  case 'RelayLiveResolver':
280
- _this2._checkResolver(selection, dataID);
281
+ if (!_this2._useExecTimeResolvers) {
282
+ _this2._checkResolver(selection, dataID);
283
+ }
281
284
  break;
282
285
  case 'ClientEdgeToClientObject':
283
- _this2._checkResolver(selection.backingField, dataID);
286
+ if (!_this2._useExecTimeResolvers) {
287
+ _this2._checkResolver(selection.backingField, dataID);
288
+ }
284
289
  break;
285
290
  default:
286
291
  selection;
@@ -31,26 +31,27 @@ function execute(config) {
31
31
  return new Executor(config);
32
32
  }
33
33
  var Executor = /*#__PURE__*/function () {
34
- function Executor(_ref2) {
34
+ function Executor(_ref3) {
35
35
  var _this = this;
36
- var actorIdentifier = _ref2.actorIdentifier,
37
- getDataID = _ref2.getDataID,
38
- getPublishQueue = _ref2.getPublishQueue,
39
- getStore = _ref2.getStore,
40
- isClientPayload = _ref2.isClientPayload,
41
- operation = _ref2.operation,
42
- operationExecutions = _ref2.operationExecutions,
43
- operationLoader = _ref2.operationLoader,
44
- operationTracker = _ref2.operationTracker,
45
- optimisticConfig = _ref2.optimisticConfig,
46
- scheduler = _ref2.scheduler,
47
- shouldProcessClientComponents = _ref2.shouldProcessClientComponents,
48
- sink = _ref2.sink,
49
- source = _ref2.source,
50
- treatMissingFieldsAsNull = _ref2.treatMissingFieldsAsNull,
51
- updater = _ref2.updater,
52
- log = _ref2.log,
53
- normalizeResponse = _ref2.normalizeResponse;
36
+ var _ref, _this$_operation$requ, _this$_operation$requ2;
37
+ var actorIdentifier = _ref3.actorIdentifier,
38
+ getDataID = _ref3.getDataID,
39
+ getPublishQueue = _ref3.getPublishQueue,
40
+ getStore = _ref3.getStore,
41
+ isClientPayload = _ref3.isClientPayload,
42
+ operation = _ref3.operation,
43
+ operationExecutions = _ref3.operationExecutions,
44
+ operationLoader = _ref3.operationLoader,
45
+ operationTracker = _ref3.operationTracker,
46
+ optimisticConfig = _ref3.optimisticConfig,
47
+ scheduler = _ref3.scheduler,
48
+ shouldProcessClientComponents = _ref3.shouldProcessClientComponents,
49
+ sink = _ref3.sink,
50
+ source = _ref3.source,
51
+ treatMissingFieldsAsNull = _ref3.treatMissingFieldsAsNull,
52
+ updater = _ref3.updater,
53
+ log = _ref3.log,
54
+ normalizeResponse = _ref3.normalizeResponse;
54
55
  this._actorIdentifier = actorIdentifier;
55
56
  this._getDataID = getDataID;
56
57
  this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
@@ -65,6 +66,8 @@ var Executor = /*#__PURE__*/function () {
65
66
  this._operationTracker = operationTracker;
66
67
  this._operationUpdateEpochs = new Map();
67
68
  this._optimisticUpdates = null;
69
+ this._useExecTimeResolvers = (_ref = (_this$_operation$requ = this._operation.request.node.operation.use_exec_time_resolvers) !== null && _this$_operation$requ !== void 0 ? _this$_operation$requ : ((_this$_operation$requ2 = this._operation.request.node.operation.exec_time_resolvers_enabled_provider) === null || _this$_operation$requ2 === void 0 ? void 0 : _this$_operation$requ2.get()) === true) !== null && _ref !== void 0 ? _ref : false;
70
+ this._execTimeResolverResponseComplete = false;
68
71
  this._pendingModulePayloadsCount = 0;
69
72
  this._getPublishQueue = getPublishQueue;
70
73
  this._scheduler = scheduler;
@@ -102,14 +105,14 @@ var Executor = /*#__PURE__*/function () {
102
105
  }
103
106
  },
104
107
  start: function start(subscription) {
105
- var _this$_operation$requ;
108
+ var _this$_operation$requ3;
106
109
  _this._start(id, subscription);
107
110
  _this._log({
108
111
  name: 'execute.start',
109
112
  executeId: _this._executeId,
110
113
  params: _this._operation.request.node.params,
111
114
  variables: _this._operation.request.variables,
112
- cacheConfig: (_this$_operation$requ = _this._operation.request.cacheConfig) !== null && _this$_operation$requ !== void 0 ? _this$_operation$requ : {}
115
+ cacheConfig: (_this$_operation$requ3 = _this._operation.request.cacheConfig) !== null && _this$_operation$requ3 !== void 0 ? _this$_operation$requ3 : {}
113
116
  });
114
117
  },
115
118
  unsubscribe: function unsubscribe() {
@@ -176,7 +179,7 @@ var Executor = /*#__PURE__*/function () {
176
179
  }
177
180
  case 'loading_final':
178
181
  {
179
- activeState = this._pendingModulePayloadsCount > 0 ? 'active' : 'inactive';
182
+ activeState = this._pendingModulePayloadsCount > 0 || this._useExecTimeResolvers && !this._execTimeResolverResponseComplete ? 'active' : 'inactive';
180
183
  break;
181
184
  }
182
185
  default:
@@ -269,8 +272,8 @@ var Executor = /*#__PURE__*/function () {
269
272
  return;
270
273
  } else if (response.data == null) {
271
274
  var errors = response.hasOwnProperty('errors') && response.errors != null ? response.errors : null;
272
- var messages = errors ? errors.map(function (_ref3) {
273
- var message = _ref3.message;
275
+ var messages = errors ? errors.map(function (_ref4) {
276
+ var message = _ref4.message;
274
277
  return message;
275
278
  }).join('\n') : '(No errors)';
276
279
  var error = RelayError.create('RelayNetwork', 'No data returned for operation `' + _this5._operation.request.node.params.name + '`, got error(s):\n' + messages + '\n\nSee the error `source` property for more information.');
@@ -337,8 +340,10 @@ var Executor = /*#__PURE__*/function () {
337
340
  }
338
341
  var _partitionGraphQLResp = partitionGraphQLResponses(responsesWithData),
339
342
  nonIncrementalResponses = _partitionGraphQLResp[0],
340
- incrementalResponses = _partitionGraphQLResp[1];
343
+ incrementalResponses = _partitionGraphQLResp[1],
344
+ normalizedResponses = _partitionGraphQLResp[2];
341
345
  var hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
346
+ var hasNormalizedResponses = normalizedResponses.length > 0;
342
347
  if (hasNonIncrementalResponses) {
343
348
  if (this._isSubscriptionOperation) {
344
349
  var nextID = generateUniqueClientID();
@@ -351,9 +356,32 @@ var Executor = /*#__PURE__*/function () {
351
356
  var payloadFollowups = this._processResponses(nonIncrementalResponses);
352
357
  this._processPayloadFollowups(payloadFollowups);
353
358
  }
359
+ if (hasNormalizedResponses) {
360
+ var _payloadFollowups = [];
361
+ for (var i = 0; i < normalizedResponses.length; i++) {
362
+ var _response$extensions2;
363
+ var _response = normalizedResponses[i];
364
+ var source = new RelayRecordSource(_response.data);
365
+ var isFinal = ((_response$extensions2 = _response.extensions) === null || _response$extensions2 === void 0 ? void 0 : _response$extensions2.is_final) === true;
366
+ var payload = {
367
+ errors: [],
368
+ fieldPayloads: [],
369
+ followupPayloads: [],
370
+ incrementalPlaceholders: [],
371
+ isFinal: isFinal,
372
+ source: source
373
+ };
374
+ this._getPublishQueueAndSaveActor().commitPayload(this._operation, payload, this._updater);
375
+ _payloadFollowups.push(payload);
376
+ this._execTimeResolverResponseComplete = isFinal;
377
+ if (isFinal) {
378
+ this._updateActiveState();
379
+ }
380
+ }
381
+ }
354
382
  if (incrementalResponses.length > 0) {
355
- var _payloadFollowups = this._processIncrementalResponses(incrementalResponses);
356
- this._processPayloadFollowups(_payloadFollowups);
383
+ var _payloadFollowups2 = this._processIncrementalResponses(incrementalResponses);
384
+ this._processPayloadFollowups(_payloadFollowups2);
357
385
  }
358
386
  if (this._isSubscriptionOperation) {
359
387
  if (responsesWithData[0].extensions == null) {
@@ -364,7 +392,7 @@ var Executor = /*#__PURE__*/function () {
364
392
  responsesWithData[0].extensions.__relay_subscription_root_id = this._operation.fragment.dataID;
365
393
  }
366
394
  }
367
- var updatedOwners = this._runPublishQueue(hasNonIncrementalResponses ? this._operation : undefined);
395
+ var updatedOwners = this._runPublishQueue(hasNonIncrementalResponses || hasNormalizedResponses ? this._operation : undefined);
368
396
  if (hasNonIncrementalResponses) {
369
397
  if (this._incrementalPayloadsPending) {
370
398
  this._retainData();
@@ -388,7 +416,7 @@ var Executor = /*#__PURE__*/function () {
388
416
  path: [],
389
417
  shouldProcessClientComponents: this._shouldProcessClientComponents,
390
418
  treatMissingFieldsAsNull: treatMissingFieldsAsNull
391
- });
419
+ }, this._useExecTimeResolvers);
392
420
  validateOptimisticResponsePayload(payload);
393
421
  optimisticUpdates.push({
394
422
  operation: this._operation,
@@ -461,16 +489,20 @@ var Executor = /*#__PURE__*/function () {
461
489
  variables = followupPayload.variables;
462
490
  }
463
491
  var selector = createNormalizationSelector(normalizationNode, followupPayload.dataID, variables);
464
- return this._normalizeResponse({
465
- data: followupPayload.data
466
- }, selector, followupPayload.typeName, {
492
+ var nextResponse = {
493
+ data: followupPayload.data,
494
+ extensions: this._state === 'loading_final' ? {
495
+ is_final: true
496
+ } : undefined
497
+ };
498
+ return this._normalizeResponse(nextResponse, selector, followupPayload.typeName, {
467
499
  actorIdentifier: this._actorIdentifier,
468
500
  getDataID: this._getDataID,
469
501
  log: this._log,
470
502
  path: followupPayload.path,
471
503
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
472
504
  shouldProcessClientComponents: this._shouldProcessClientComponents
473
- });
505
+ }, this._useExecTimeResolvers);
474
506
  };
475
507
  _proto._processOptimisticModuleImport = function _processOptimisticModuleImport(normalizationRootNode, moduleImportPayload) {
476
508
  var operation = getOperation(normalizationRootNode);
@@ -527,7 +559,7 @@ var Executor = /*#__PURE__*/function () {
527
559
  path: [],
528
560
  treatMissingFieldsAsNull: _this8._treatMissingFieldsAsNull,
529
561
  shouldProcessClientComponents: _this8._shouldProcessClientComponents
530
- });
562
+ }, _this8._useExecTimeResolvers);
531
563
  _this8._getPublishQueueAndSaveActor().commitPayload(_this8._operation, relayPayload, _this8._updater);
532
564
  _this8._log({
533
565
  name: 'execute.normalize.end',
@@ -574,7 +606,10 @@ var Executor = /*#__PURE__*/function () {
574
606
  incrementalPlaceholders.forEach(function (placeholder) {
575
607
  if (placeholder.kind === 'defer') {
576
608
  relayPayloads.push(_this9._processDeferResponse(placeholder.label, placeholder.path, placeholder, {
577
- data: placeholder.data
609
+ data: placeholder.data,
610
+ extensions: {
611
+ is_final: true
612
+ }
578
613
  }));
579
614
  }
580
615
  });
@@ -586,7 +621,7 @@ var Executor = /*#__PURE__*/function () {
586
621
  });
587
622
  };
588
623
  _proto._maybeCompleteSubscriptionOperationTracking = function _maybeCompleteSubscriptionOperationTracking() {
589
- if (!this._isSubscriptionOperation) {
624
+ if (!this._isSubscriptionOperation && !(this._useExecTimeResolvers && this._execTimeResolverResponseComplete && this._state === 'loading_final')) {
590
625
  return;
591
626
  }
592
627
  if (this._pendingModulePayloadsCount === 0 && this._incrementalPayloadsPending === false) {
@@ -814,20 +849,20 @@ var Executor = /*#__PURE__*/function () {
814
849
  path: placeholder.path,
815
850
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
816
851
  shouldProcessClientComponents: this._shouldProcessClientComponents
817
- });
852
+ }, this._useExecTimeResolvers);
818
853
  this._getPublishQueueAndSaveActor().commitPayload(this._operation, relayPayload);
819
854
  var parentEntry = this._source.get(parentID);
820
855
  !(parentEntry != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'OperationExecutor: Expected the parent record `%s` for @defer ' + 'data to exist.', parentID) : invariant(false) : void 0;
821
856
  var fieldPayloads = parentEntry.fieldPayloads;
822
857
  if (fieldPayloads.length !== 0) {
823
- var _response$extensions2;
858
+ var _response$extensions3;
824
859
  var handleFieldsRelayPayload = {
825
860
  errors: null,
826
861
  fieldPayloads: fieldPayloads,
827
862
  incrementalPlaceholders: null,
828
863
  followupPayloads: null,
829
864
  source: RelayRecordSource.create(),
830
- isFinal: ((_response$extensions2 = response.extensions) === null || _response$extensions2 === void 0 ? void 0 : _response$extensions2.is_final) === true
865
+ isFinal: ((_response$extensions3 = response.extensions) === null || _response$extensions3 === void 0 ? void 0 : _response$extensions3.is_final) === true
831
866
  };
832
867
  this._getPublishQueueAndSaveActor().commitPayload(this._operation, handleFieldsRelayPayload);
833
868
  }
@@ -883,7 +918,7 @@ var Executor = /*#__PURE__*/function () {
883
918
  return relayPayload;
884
919
  };
885
920
  _proto._normalizeStreamItem = function _normalizeStreamItem(response, parentID, field, variables, path, normalizationPath) {
886
- var _field$alias, _field$concreteType, _ref, _this$_getDataID;
921
+ var _field$alias, _field$concreteType, _ref2, _this$_getDataID;
887
922
  var data = response.data;
888
923
  !(typeof data === 'object') ? process.env.NODE_ENV !== "production" ? invariant(false, 'OperationExecutor: Expected the GraphQL @stream payload `data` ' + 'value to be an object.') : invariant(false) : void 0;
889
924
  var responseKey = (_field$alias = field.alias) !== null && _field$alias !== void 0 ? _field$alias : field.name;
@@ -899,7 +934,7 @@ var Executor = /*#__PURE__*/function () {
899
934
  !(itemIndex === finalPathEntry && itemIndex >= 0) ? process.env.NODE_ENV !== "production" ? invariant(false, 'OperationExecutor: Expected path for @stream to end in a ' + 'positive integer index, got `%s`', finalPathEntry) : invariant(false) : void 0;
900
935
  var typeName = (_field$concreteType = field.concreteType) !== null && _field$concreteType !== void 0 ? _field$concreteType : data[TYPENAME_KEY];
901
936
  !(typeof typeName === 'string') ? process.env.NODE_ENV !== "production" ? invariant(false, 'OperationExecutor: Expected @stream field `%s` to have a ' + '__typename.', field.name) : invariant(false) : void 0;
902
- var itemID = (_ref = (_this$_getDataID = this._getDataID(data, typeName)) !== null && _this$_getDataID !== void 0 ? _this$_getDataID : prevIDs === null || prevIDs === void 0 ? void 0 : prevIDs[itemIndex]) !== null && _ref !== void 0 ? _ref : generateClientID(parentID, storageKey, itemIndex);
937
+ var itemID = (_ref2 = (_this$_getDataID = this._getDataID(data, typeName)) !== null && _this$_getDataID !== void 0 ? _this$_getDataID : prevIDs === null || prevIDs === void 0 ? void 0 : prevIDs[itemIndex]) !== null && _ref2 !== void 0 ? _ref2 : generateClientID(parentID, storageKey, itemIndex);
903
938
  !(typeof itemID === 'string') ? process.env.NODE_ENV !== "production" ? invariant(false, 'OperationExecutor: Expected id of elements of field `%s` to ' + 'be strings.', storageKey) : invariant(false) : void 0;
904
939
  var selector = createNormalizationSelector(field, itemID, variables);
905
940
  var nextParentRecord = RelayModernRecord.clone(parentRecord);
@@ -917,7 +952,7 @@ var Executor = /*#__PURE__*/function () {
917
952
  path: [].concat((0, _toConsumableArray2["default"])(normalizationPath), [responseKey, String(itemIndex)]),
918
953
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
919
954
  shouldProcessClientComponents: this._shouldProcessClientComponents
920
- });
955
+ }, this._useExecTimeResolvers);
921
956
  return {
922
957
  fieldPayloads: fieldPayloads,
923
958
  itemID: itemID,
@@ -1031,7 +1066,9 @@ var Executor = /*#__PURE__*/function () {
1031
1066
  function partitionGraphQLResponses(responses) {
1032
1067
  var nonIncrementalResponses = [];
1033
1068
  var incrementalResponses = [];
1069
+ var normalizedResponses = [];
1034
1070
  responses.forEach(function (response) {
1071
+ var _response$extensions4;
1035
1072
  if (response.path != null || response.label != null) {
1036
1073
  var label = response.label,
1037
1074
  path = response.path;
@@ -1043,11 +1080,13 @@ function partitionGraphQLResponses(responses) {
1043
1080
  path: path,
1044
1081
  response: response
1045
1082
  });
1083
+ } else if (((_response$extensions4 = response.extensions) === null || _response$extensions4 === void 0 ? void 0 : _response$extensions4.is_normalized) === true) {
1084
+ normalizedResponses.push(response);
1046
1085
  } else {
1047
1086
  nonIncrementalResponses.push(response);
1048
1087
  }
1049
1088
  });
1050
- return [nonIncrementalResponses, incrementalResponses];
1089
+ return [nonIncrementalResponses, incrementalResponses, normalizedResponses];
1051
1090
  }
1052
1091
  function stableStringify(value) {
1053
1092
  var _JSON$stringify;
@@ -108,10 +108,11 @@ var RelayModernStore = /*#__PURE__*/function () {
108
108
  }
109
109
  };
110
110
  _proto.check = function check(operation, options) {
111
- var _options$handlers, _options$getSourceFor, _options$getTargetFor, _options$defaultActor;
111
+ var _ref, _operation$request$no, _operation$request$no2, _options$handlers, _options$getSourceFor, _options$getTargetFor, _options$defaultActor;
112
112
  var selector = operation.root;
113
113
  var source = this._getMutableRecordSource();
114
114
  var globalInvalidationEpoch = this._globalInvalidationEpoch;
115
+ var useExecTimeResolvers = (_ref = (_operation$request$no = operation.request.node.operation.use_exec_time_resolvers) !== null && _operation$request$no !== void 0 ? _operation$request$no : ((_operation$request$no2 = operation.request.node.operation.exec_time_resolvers_enabled_provider) === null || _operation$request$no2 === void 0 ? void 0 : _operation$request$no2.get()) === true) !== null && _ref !== void 0 ? _ref : false;
115
116
  var rootEntry = this._roots.get(operation.request.identifier);
116
117
  var operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null;
117
118
  if (globalInvalidationEpoch != null) {
@@ -130,7 +131,7 @@ var RelayModernStore = /*#__PURE__*/function () {
130
131
  assertInternalActorIdentifier(actorIdentifier);
131
132
  return source;
132
133
  };
133
- var operationAvailability = DataChecker.check(getSourceForActor, getTargetForActor, (_options$defaultActor = options === null || options === void 0 ? void 0 : options.defaultActorIdentifier) !== null && _options$defaultActor !== void 0 ? _options$defaultActor : INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, selector, handlers, this._operationLoader, this._getDataID, this._shouldProcessClientComponents, this.__log);
134
+ var operationAvailability = DataChecker.check(getSourceForActor, getTargetForActor, (_options$defaultActor = options === null || options === void 0 ? void 0 : options.defaultActorIdentifier) !== null && _options$defaultActor !== void 0 ? _options$defaultActor : INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, selector, handlers, this._operationLoader, this._getDataID, this._shouldProcessClientComponents, this.__log, useExecTimeResolvers);
134
135
  return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
135
136
  };
136
137
  _proto.retain = function retain(operation) {
@@ -435,6 +436,7 @@ var RelayModernStore = /*#__PURE__*/function () {
435
436
  _step2;
436
437
  try {
437
438
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
439
+ var _ref2, _operation$request$no3, _operation$request$no4;
438
440
  var _step2$value = _step2.value,
439
441
  _dataID = _step2$value[0],
440
442
  _step2$value$ = _step2$value[1],
@@ -451,7 +453,8 @@ var RelayModernStore = /*#__PURE__*/function () {
451
453
  }
452
454
  }
453
455
  var selector = operation.root;
454
- RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader, this._shouldProcessClientComponents);
456
+ var useExecTimeResolvers = (_ref2 = (_operation$request$no3 = operation.request.node.operation.use_exec_time_resolvers) !== null && _operation$request$no3 !== void 0 ? _operation$request$no3 : ((_operation$request$no4 = operation.request.node.operation.exec_time_resolvers_enabled_provider) === null || _operation$request$no4 === void 0 ? void 0 : _operation$request$no4.get()) === true) !== null && _ref2 !== void 0 ? _ref2 : false;
457
+ RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader, this._shouldProcessClientComponents, useExecTimeResolvers);
455
458
  yield;
456
459
  if (startEpoch !== this._currentWriteEpoch) {
457
460
  if (log != null) {
@@ -470,6 +470,9 @@ var RelayReader = /*#__PURE__*/function () {
470
470
  __fragmentOwner: _this._owner,
471
471
  __fragments: (0, _defineProperty2["default"])({}, fragment.name, fragment.args ? getArgumentValues(fragment.args, _this._variables) : {})
472
472
  };
473
+ if (_this._clientEdgeTraversalPath.length > 0 && _this._clientEdgeTraversalPath[_this._clientEdgeTraversalPath.length - 1] !== null) {
474
+ key[CLIENT_EDGE_TRAVERSAL_PATH] = (0, _toConsumableArray2["default"])(_this._clientEdgeTraversalPath);
475
+ }
473
476
  var resolverContext = {
474
477
  getDataForResolverFragment: getDataForResolverFragment
475
478
  };
@@ -15,17 +15,18 @@ var invariant = require('invariant');
15
15
  var getReadTimeResolverStorageKey = RelayStoreUtils.getReadTimeResolverStorageKey,
16
16
  getStorageKey = RelayStoreUtils.getStorageKey,
17
17
  getModuleOperationKey = RelayStoreUtils.getModuleOperationKey;
18
- function mark(recordSource, selector, references, operationLoader, shouldProcessClientComponents) {
18
+ function mark(recordSource, selector, references, operationLoader, shouldProcessClientComponents, useExecTimeResolvers) {
19
19
  var dataID = selector.dataID,
20
20
  node = selector.node,
21
21
  variables = selector.variables;
22
- var marker = new RelayReferenceMarker(recordSource, variables, references, operationLoader, shouldProcessClientComponents);
22
+ var marker = new RelayReferenceMarker(recordSource, variables, references, operationLoader, shouldProcessClientComponents, useExecTimeResolvers);
23
23
  marker.mark(node, dataID);
24
24
  }
25
25
  var RelayReferenceMarker = /*#__PURE__*/function () {
26
- function RelayReferenceMarker(recordSource, variables, references, operationLoader, shouldProcessClientComponents) {
26
+ function RelayReferenceMarker(recordSource, variables, references, operationLoader, shouldProcessClientComponents, useExecTimeResolvers) {
27
27
  this._operationLoader = operationLoader !== null && operationLoader !== void 0 ? operationLoader : null;
28
28
  this._operationName = null;
29
+ this._useExecTimeResolvers = useExecTimeResolvers !== null && useExecTimeResolvers !== void 0 ? useExecTimeResolvers : false;
29
30
  this._recordSource = recordSource;
30
31
  this._references = references;
31
32
  this._variables = variables;
@@ -137,6 +138,10 @@ var RelayReferenceMarker = /*#__PURE__*/function () {
137
138
  });
138
139
  };
139
140
  _proto._traverseClientEdgeToClientObject = function _traverseClientEdgeToClientObject(field, record) {
141
+ if (this._useExecTimeResolvers) {
142
+ this._traverseLink(field.linkedField, record);
143
+ return;
144
+ }
140
145
  var dataID = this._traverseResolverField(field.backingField, record);
141
146
  if (dataID == null) {
142
147
  return;
@@ -194,6 +199,9 @@ var RelayReferenceMarker = /*#__PURE__*/function () {
194
199
  }
195
200
  };
196
201
  _proto._traverseResolverField = function _traverseResolverField(field, record) {
202
+ if (this._useExecTimeResolvers) {
203
+ return;
204
+ }
197
205
  var storageKey = getReadTimeResolverStorageKey(field, this._variables);
198
206
  var dataID = RelayModernRecord.getLinkedRecordID(record, storageKey);
199
207
  if (dataID != null) {
@@ -33,15 +33,15 @@ var _require7 = require('./TypeID'),
33
33
  var areEqual = require("fbjs/lib/areEqual");
34
34
  var invariant = require('invariant');
35
35
  var warning = require("fbjs/lib/warning");
36
- function normalize(recordSource, selector, response, options, errors) {
36
+ function normalize(recordSource, selector, response, options, errors, useExecTimeResolvers) {
37
37
  var dataID = selector.dataID,
38
38
  node = selector.node,
39
39
  variables = selector.variables;
40
- var normalizer = new RelayResponseNormalizer(recordSource, variables, options);
40
+ var normalizer = new RelayResponseNormalizer(recordSource, variables, options, useExecTimeResolvers !== null && useExecTimeResolvers !== void 0 ? useExecTimeResolvers : false);
41
41
  return normalizer.normalizeResponse(node, dataID, response, errors);
42
42
  }
43
43
  var RelayResponseNormalizer = /*#__PURE__*/function () {
44
- function RelayResponseNormalizer(recordSource, variables, options) {
44
+ function RelayResponseNormalizer(recordSource, variables, options, useExecTimeResolvers) {
45
45
  this._actorIdentifier = options.actorIdentifier;
46
46
  this._getDataId = options.getDataID;
47
47
  this._handleFieldPayloads = [];
@@ -49,6 +49,7 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
49
49
  this._incrementalPlaceholders = [];
50
50
  this._isClientExtension = false;
51
51
  this._isUnmatchedAbstractType = false;
52
+ this._useExecTimeResolvers = useExecTimeResolvers;
52
53
  this._followupPayloads = [];
53
54
  this._path = options.path ? (0, _toConsumableArray2["default"])(options.path) : [];
54
55
  this._recordSource = recordSource;
@@ -207,10 +208,14 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
207
208
  break;
208
209
  case 'RelayResolver':
209
210
  case 'RelayLiveResolver':
210
- this._normalizeResolver(selection, record, data);
211
+ if (!this._useExecTimeResolvers) {
212
+ this._normalizeResolver(selection, record, data);
213
+ }
211
214
  break;
212
215
  case 'ClientEdgeToClientObject':
213
- this._normalizeResolver(selection.backingField, record, data);
216
+ if (!this._useExecTimeResolvers) {
217
+ this._normalizeResolver(selection.backingField, record, data);
218
+ }
214
219
  break;
215
220
  default:
216
221
  selection;
@@ -368,8 +368,9 @@ var LiveResolverCache = /*#__PURE__*/function () {
368
368
  var record = RelayModernRecord.create(outputTypeDataID, typename);
369
369
  source.set(outputTypeDataID, record);
370
370
  var selector = createNormalizationSelector(normalizationInfo.normalizationNode, outputTypeDataID, variables);
371
+ var useExecTimeResolvers = false;
371
372
  var normalizationOptions = this._store.__getNormalizationOptions(fieldPath);
372
- return normalize(source, selector, value, normalizationOptions).source;
373
+ return normalize(source, selector, value, normalizationOptions, undefined, useExecTimeResolvers).source;
373
374
  }
374
375
  case 'WeakModel':
375
376
  {
@@ -5,14 +5,14 @@ var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/obje
5
5
  var _RelayModernRecord = _interopRequireDefault(require("./RelayModernRecord"));
6
6
  var _RelayRecordSource = _interopRequireDefault(require("./RelayRecordSource"));
7
7
  var _RelayResponseNormalizer = _interopRequireDefault(require("./RelayResponseNormalizer"));
8
- function normalizeResponse(response, selector, typeName, options) {
8
+ function normalizeResponse(response, selector, typeName, options, useExecTimeResolvers) {
9
9
  var _response$extensions;
10
10
  var data = response.data,
11
11
  errors = response.errors;
12
12
  var source = _RelayRecordSource["default"].create();
13
13
  var record = _RelayModernRecord["default"].create(selector.dataID, typeName);
14
14
  source.set(selector.dataID, record);
15
- var relayPayload = _RelayResponseNormalizer["default"].normalize(source, selector, data, options, errors);
15
+ var relayPayload = _RelayResponseNormalizer["default"].normalize(source, selector, data, options, errors, useExecTimeResolvers);
16
16
  return (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, relayPayload), {}, {
17
17
  isFinal: ((_response$extensions = response.extensions) === null || _response$extensions === void 0 ? void 0 : _response$extensions.is_final) === true
18
18
  });
@@ -25,6 +25,7 @@ var RelayFeatureFlags = {
25
25
  ENABLE_USE_PAGINATION_IS_LOADING_FIX: false,
26
26
  DISALLOW_NESTED_UPDATES: false,
27
27
  ENABLE_TYPENAME_PREFIXED_DATA_ID: false,
28
- ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false
28
+ ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false,
29
+ CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false
29
30
  };
30
31
  module.exports = RelayFeatureFlags;
@@ -37,7 +37,7 @@ const validateMutation = require('./validateMutation');
37
37
  const invariant = require('invariant');
38
38
  const warning = require('warning');
39
39
 
40
- export type MutationConfig<TMutation: MutationParameters> = {
40
+ export type MutationConfig<TMutation: MutationParameters> = $ReadOnly<{
41
41
  cacheConfig?: CacheConfig,
42
42
  configs?: Array<DeclarativeMutationConfig>,
43
43
  mutation: GraphQLTaggedNode,
@@ -57,9 +57,9 @@ export type MutationConfig<TMutation: MutationParameters> = {
57
57
  updater?: ?SelectorStoreUpdater<TMutation['response']>,
58
58
  uploadables?: UploadableMap,
59
59
  variables: TMutation['variables'],
60
- };
60
+ }>;
61
61
 
62
- export type CommitMutationConfig<TVariables, TData, TRawResponse> = {
62
+ export type CommitMutationConfig<TVariables, TData, TRawResponse> = $ReadOnly<{
63
63
  cacheConfig?: CacheConfig,
64
64
  configs?: Array<DeclarativeMutationConfig>,
65
65
  mutation: Mutation<TVariables, TData, TRawResponse>,
@@ -71,8 +71,8 @@ export type CommitMutationConfig<TVariables, TData, TRawResponse> = {
71
71
  optimisticUpdater?: ?SelectorStoreUpdater<TData>,
72
72
  updater?: ?SelectorStoreUpdater<TData>,
73
73
  uploadables?: UploadableMap,
74
- variables: TVariables,
75
- };
74
+ variables: NoInfer<TVariables>,
75
+ }>;
76
76
 
77
77
  /**
78
78
  * Higher-level helper function to execute a mutation against a specific
@@ -26,7 +26,7 @@ export interface INetwork {
26
26
 
27
27
  export type LogRequestInfoFunction = mixed => void;
28
28
 
29
- export type PayloadData = {[key: string]: mixed};
29
+ export type PayloadData = {+[key: string]: mixed};
30
30
 
31
31
  export type PayloadError = interface {
32
32
  message: string,
@@ -134,4 +134,4 @@ export type SubscribeFunction = (
134
134
  ) => RelayObservable<GraphQLResponse>;
135
135
 
136
136
  export type Uploadable = File | Blob;
137
- export type UploadableMap = {[key: string]: Uploadable};
137
+ export type UploadableMap = {+[key: string]: Uploadable};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "relay-runtime",
3
3
  "description": "A core runtime for building GraphQL-driven applications.",
4
- "version": "19.0.0",
4
+ "version": "20.1.0",
5
5
  "keywords": [
6
6
  "graphql",
7
7
  "relay"
@@ -115,7 +115,7 @@ const invariant = require('invariant');
115
115
  function fetchQuery<TVariables: Variables, TData, TRawResponse>(
116
116
  environment: IEnvironment,
117
117
  query: Query<TVariables, TData, TRawResponse>,
118
- variables: TVariables,
118
+ variables: NoInfer<TVariables>,
119
119
  options?: $ReadOnly<{
120
120
  fetchPolicy?: FetchQueryFetchPolicy,
121
121
  networkCacheConfig?: CacheConfig,
@@ -73,6 +73,7 @@ function check(
73
73
  getDataID: GetDataID,
74
74
  shouldProcessClientComponents: ?boolean,
75
75
  log: ?LogFunction,
76
+ useExecTimeResolvers: ?boolean,
76
77
  ): Availability {
77
78
  if (log != null) {
78
79
  log({
@@ -91,6 +92,7 @@ function check(
91
92
  getDataID,
92
93
  shouldProcessClientComponents,
93
94
  log,
95
+ useExecTimeResolvers,
94
96
  );
95
97
  const result = checker.check(node, dataID);
96
98
  if (log != null) {
@@ -113,6 +115,7 @@ class DataChecker {
113
115
  _recordSourceProxy: RelayRecordSourceProxy;
114
116
  _recordWasMissing: boolean;
115
117
  _source: RecordSource;
118
+ _useExecTimeResolvers: boolean;
116
119
  _variables: Variables;
117
120
  _shouldProcessClientComponents: ?boolean;
118
121
  +_getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource;
@@ -138,6 +141,7 @@ class DataChecker {
138
141
  getDataID: GetDataID,
139
142
  shouldProcessClientComponents: ?boolean,
140
143
  log: ?LogFunction,
144
+ useExecTimeResolvers: ?boolean,
141
145
  ) {
142
146
  this._getSourceForActor = getSourceForActor;
143
147
  this._getTargetForActor = getTargetForActor;
@@ -147,6 +151,7 @@ class DataChecker {
147
151
  const [mutator, recordSourceProxy] = this._getMutatorAndRecordProxyForActor(
148
152
  defaultActorIdentifier,
149
153
  );
154
+ this._useExecTimeResolvers = useExecTimeResolvers ?? false;
150
155
  this._mostRecentlyInvalidatedAt = null;
151
156
  this._handlers = handlers;
152
157
  this._mutator = mutator;
@@ -455,10 +460,14 @@ class DataChecker {
455
460
  break;
456
461
  case 'RelayResolver':
457
462
  case 'RelayLiveResolver':
458
- this._checkResolver(selection, dataID);
463
+ if (!this._useExecTimeResolvers) {
464
+ this._checkResolver(selection, dataID);
465
+ }
459
466
  break;
460
467
  case 'ClientEdgeToClientObject':
461
- this._checkResolver(selection.backingField, dataID);
468
+ if (!this._useExecTimeResolvers) {
469
+ this._checkResolver(selection.backingField, dataID);
470
+ }
462
471
  break;
463
472
  default:
464
473
  (selection: empty);
@@ -138,6 +138,7 @@ class Executor<TMutation: MutationParameters> {
138
138
  _operationTracker: OperationTracker;
139
139
  _operationUpdateEpochs: Map<string, number>;
140
140
  _optimisticUpdates: null | Array<OptimisticUpdate<TMutation>>;
141
+ _useExecTimeResolvers: boolean;
141
142
  _pendingModulePayloadsCount: number;
142
143
  +_getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue;
143
144
  _shouldProcessClientComponents: ?boolean;
@@ -158,6 +159,7 @@ class Executor<TMutation: MutationParameters> {
158
159
  +_isSubscriptionOperation: boolean;
159
160
  +_seenActors: Set<ActorIdentifier>;
160
161
  _normalizeResponse: NormalizeResponseFunction;
162
+ _execTimeResolverResponseComplete: boolean;
161
163
 
162
164
  constructor({
163
165
  actorIdentifier,
@@ -193,6 +195,12 @@ class Executor<TMutation: MutationParameters> {
193
195
  this._operationTracker = operationTracker;
194
196
  this._operationUpdateEpochs = new Map();
195
197
  this._optimisticUpdates = null;
198
+ this._useExecTimeResolvers =
199
+ this._operation.request.node.operation.use_exec_time_resolvers ??
200
+ this._operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() ===
201
+ true ??
202
+ false;
203
+ this._execTimeResolverResponseComplete = false;
196
204
  this._pendingModulePayloadsCount = 0;
197
205
  this._getPublishQueue = getPublishQueue;
198
206
  this._scheduler = scheduler;
@@ -315,7 +323,11 @@ class Executor<TMutation: MutationParameters> {
315
323
  }
316
324
  case 'loading_final': {
317
325
  activeState =
318
- this._pendingModulePayloadsCount > 0 ? 'active' : 'inactive';
326
+ this._pendingModulePayloadsCount > 0 ||
327
+ (this._useExecTimeResolvers &&
328
+ !this._execTimeResolverResponseComplete)
329
+ ? 'active'
330
+ : 'inactive';
319
331
  break;
320
332
  }
321
333
  default:
@@ -516,9 +528,10 @@ class Executor<TMutation: MutationParameters> {
516
528
  return;
517
529
  }
518
530
 
519
- const [nonIncrementalResponses, incrementalResponses] =
531
+ const [nonIncrementalResponses, incrementalResponses, normalizedResponses] =
520
532
  partitionGraphQLResponses(responsesWithData);
521
533
  const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
534
+ const hasNormalizedResponses = normalizedResponses.length > 0;
522
535
 
523
536
  // In theory this doesn't preserve the ordering of the batch.
524
537
  // The idea is that a batch is always:
@@ -553,6 +566,37 @@ class Executor<TMutation: MutationParameters> {
553
566
  this._processPayloadFollowups(payloadFollowups);
554
567
  }
555
568
 
569
+ if (hasNormalizedResponses) {
570
+ const payloadFollowups = [];
571
+ for (let i = 0; i < normalizedResponses.length; i++) {
572
+ const response = normalizedResponses[i];
573
+ const source = new RelayRecordSource(
574
+ response.data as $FlowExpectedError,
575
+ );
576
+ const isFinal = response.extensions?.is_final === true;
577
+ const payload: RelayResponsePayload = {
578
+ errors: [],
579
+ fieldPayloads: [],
580
+ followupPayloads: [],
581
+ incrementalPlaceholders: [],
582
+ isFinal,
583
+ source,
584
+ };
585
+ this._getPublishQueueAndSaveActor().commitPayload(
586
+ this._operation,
587
+ payload,
588
+ this._updater,
589
+ );
590
+ payloadFollowups.push(payload);
591
+ this._execTimeResolverResponseComplete = isFinal;
592
+ if (isFinal) {
593
+ // Need to update the active state to mark the query as inactive,
594
+ // incase server payloads have completed
595
+ this._updateActiveState();
596
+ }
597
+ }
598
+ }
599
+
556
600
  if (incrementalResponses.length > 0) {
557
601
  const payloadFollowups =
558
602
  this._processIncrementalResponses(incrementalResponses);
@@ -578,7 +622,9 @@ class Executor<TMutation: MutationParameters> {
578
622
  // the publish queue here, which will later be passed to the store (via
579
623
  // notify) to indicate that this operation caused the store to update
580
624
  const updatedOwners = this._runPublishQueue(
581
- hasNonIncrementalResponses ? this._operation : undefined,
625
+ hasNonIncrementalResponses || hasNormalizedResponses
626
+ ? this._operation
627
+ : undefined,
582
628
  );
583
629
 
584
630
  if (hasNonIncrementalResponses) {
@@ -617,6 +663,7 @@ class Executor<TMutation: MutationParameters> {
617
663
  shouldProcessClientComponents: this._shouldProcessClientComponents,
618
664
  treatMissingFieldsAsNull,
619
665
  },
666
+ this._useExecTimeResolvers,
620
667
  );
621
668
  validateOptimisticResponsePayload(payload);
622
669
  optimisticUpdates.push({
@@ -715,8 +762,15 @@ class Executor<TMutation: MutationParameters> {
715
762
  followupPayload.dataID,
716
763
  variables,
717
764
  );
765
+ const nextResponse: GraphQLResponseWithData = {
766
+ data: followupPayload.data,
767
+ // `is_final` flag needs to be set for processing nested defer and 3D
768
+ // when the server doesn't support streaming
769
+ extensions:
770
+ this._state === 'loading_final' ? {is_final: true} : undefined,
771
+ };
718
772
  return this._normalizeResponse(
719
- {data: followupPayload.data},
773
+ nextResponse,
720
774
  selector,
721
775
  followupPayload.typeName,
722
776
  {
@@ -727,6 +781,7 @@ class Executor<TMutation: MutationParameters> {
727
781
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
728
782
  shouldProcessClientComponents: this._shouldProcessClientComponents,
729
783
  },
784
+ this._useExecTimeResolvers,
730
785
  );
731
786
  }
732
787
 
@@ -810,6 +865,7 @@ class Executor<TMutation: MutationParameters> {
810
865
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
811
866
  shouldProcessClientComponents: this._shouldProcessClientComponents,
812
867
  },
868
+ this._useExecTimeResolvers,
813
869
  );
814
870
  this._getPublishQueueAndSaveActor().commitPayload(
815
871
  this._operation,
@@ -884,7 +940,8 @@ class Executor<TMutation: MutationParameters> {
884
940
  placeholder.label,
885
941
  placeholder.path,
886
942
  placeholder,
887
- {data: placeholder.data},
943
+ // `is_final` flag needs to be set for processing nested defer payloads
944
+ {data: placeholder.data, extensions: {is_final: true}},
888
945
  ),
889
946
  );
890
947
  }
@@ -898,7 +955,14 @@ class Executor<TMutation: MutationParameters> {
898
955
  }
899
956
 
900
957
  _maybeCompleteSubscriptionOperationTracking() {
901
- if (!this._isSubscriptionOperation) {
958
+ if (
959
+ !this._isSubscriptionOperation &&
960
+ !(
961
+ this._useExecTimeResolvers &&
962
+ this._execTimeResolverResponseComplete &&
963
+ this._state === 'loading_final'
964
+ )
965
+ ) {
902
966
  return;
903
967
  }
904
968
  if (
@@ -1267,6 +1331,7 @@ class Executor<TMutation: MutationParameters> {
1267
1331
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1268
1332
  shouldProcessClientComponents: this._shouldProcessClientComponents,
1269
1333
  },
1334
+ this._useExecTimeResolvers,
1270
1335
  );
1271
1336
  this._getPublishQueueAndSaveActor().commitPayload(
1272
1337
  this._operation,
@@ -1487,14 +1552,20 @@ class Executor<TMutation: MutationParameters> {
1487
1552
  record: nextParentRecord,
1488
1553
  fieldPayloads,
1489
1554
  });
1490
- const relayPayload = this._normalizeResponse(response, selector, typeName, {
1491
- actorIdentifier: this._actorIdentifier,
1492
- getDataID: this._getDataID,
1493
- log: this._log,
1494
- path: [...normalizationPath, responseKey, String(itemIndex)],
1495
- treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1496
- shouldProcessClientComponents: this._shouldProcessClientComponents,
1497
- });
1555
+ const relayPayload = this._normalizeResponse(
1556
+ response,
1557
+ selector,
1558
+ typeName,
1559
+ {
1560
+ actorIdentifier: this._actorIdentifier,
1561
+ getDataID: this._getDataID,
1562
+ log: this._log,
1563
+ path: [...normalizationPath, responseKey, String(itemIndex)],
1564
+ treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1565
+ shouldProcessClientComponents: this._shouldProcessClientComponents,
1566
+ },
1567
+ this._useExecTimeResolvers,
1568
+ );
1498
1569
  return {
1499
1570
  fieldPayloads,
1500
1571
  itemID,
@@ -1597,9 +1668,11 @@ function partitionGraphQLResponses(
1597
1668
  ): [
1598
1669
  $ReadOnlyArray<GraphQLResponseWithData>,
1599
1670
  $ReadOnlyArray<IncrementalGraphQLResponse>,
1671
+ $ReadOnlyArray<GraphQLResponseWithData>,
1600
1672
  ] {
1601
1673
  const nonIncrementalResponses: Array<GraphQLResponseWithData> = [];
1602
1674
  const incrementalResponses: Array<IncrementalGraphQLResponse> = [];
1675
+ const normalizedResponses: Array<GraphQLResponseWithData> = [];
1603
1676
  responses.forEach(response => {
1604
1677
  if (response.path != null || response.label != null) {
1605
1678
  const {label, path} = response;
@@ -1617,11 +1690,13 @@ function partitionGraphQLResponses(
1617
1690
  path,
1618
1691
  response,
1619
1692
  });
1693
+ } else if (response.extensions?.is_normalized === true) {
1694
+ normalizedResponses.push(response);
1620
1695
  } else {
1621
1696
  nonIncrementalResponses.push(response);
1622
1697
  }
1623
1698
  });
1624
- return [nonIncrementalResponses, incrementalResponses];
1699
+ return [nonIncrementalResponses, incrementalResponses, normalizedResponses];
1625
1700
  }
1626
1701
 
1627
1702
  function stableStringify(value: mixed): string {
@@ -242,6 +242,11 @@ class RelayModernStore implements Store {
242
242
  const selector = operation.root;
243
243
  const source = this._getMutableRecordSource();
244
244
  const globalInvalidationEpoch = this._globalInvalidationEpoch;
245
+ const useExecTimeResolvers =
246
+ operation.request.node.operation.use_exec_time_resolvers ??
247
+ operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() ===
248
+ true ??
249
+ false;
245
250
 
246
251
  const rootEntry = this._roots.get(operation.request.identifier);
247
252
  const operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null;
@@ -286,6 +291,7 @@ class RelayModernStore implements Store {
286
291
  this._getDataID,
287
292
  this._shouldProcessClientComponents,
288
293
  this.__log,
294
+ useExecTimeResolvers,
289
295
  );
290
296
 
291
297
  return getAvailabilityStatus(
@@ -464,6 +470,8 @@ class RelayModernStore implements Store {
464
470
  fetchTime: Date.now(),
465
471
  };
466
472
  this._releaseBuffer.push(id);
473
+ /* $FlowFixMe[incompatible-call] Natural Inference rollout. See
474
+ * https://fburl.com/gdoc/y8dn025u */
467
475
  this._roots.set(id, temporaryRootEntry);
468
476
  }
469
477
  }
@@ -743,12 +751,18 @@ class RelayModernStore implements Store {
743
751
 
744
752
  // Mark all records that are traversable from a root that is still valid
745
753
  const selector = operation.root;
754
+ const useExecTimeResolvers =
755
+ operation.request.node.operation.use_exec_time_resolvers ??
756
+ operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() ===
757
+ true ??
758
+ false;
746
759
  RelayReferenceMarker.mark(
747
760
  this._recordSource,
748
761
  selector,
749
762
  references,
750
763
  this._operationLoader,
751
764
  this._shouldProcessClientComponents,
765
+ useExecTimeResolvers,
752
766
  );
753
767
  // Yield for other work after each operation
754
768
  yield;
@@ -788,7 +788,7 @@ class RelayReader {
788
788
  // `getResolverValue`) and converted into an error object.
789
789
  const evaluate = (): EvaluationResult<mixed> => {
790
790
  if (fragment != null) {
791
- const key = {
791
+ const key: SelectorData = {
792
792
  __id: parentRecordID,
793
793
  __fragmentOwner: this._owner,
794
794
  __fragments: {
@@ -797,6 +797,14 @@ class RelayReader {
797
797
  : {},
798
798
  },
799
799
  };
800
+ if (
801
+ this._clientEdgeTraversalPath.length > 0 &&
802
+ this._clientEdgeTraversalPath[
803
+ this._clientEdgeTraversalPath.length - 1
804
+ ] !== null
805
+ ) {
806
+ key[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
807
+ }
800
808
  const resolverContext = {getDataForResolverFragment};
801
809
  return withResolverContext(resolverContext, () => {
802
810
  const [resolverResult, resolverError] = getResolverValue(
@@ -86,7 +86,7 @@ class RelayRecordSource implements MutableRecordSource {
86
86
  }
87
87
 
88
88
  toJSON(): RecordSourceJSON {
89
- const obj: RecordSourceJSON = {};
89
+ const obj: {...RecordSourceJSON} = {};
90
90
  for (const [key, record] of this._records) {
91
91
  obj[key] = RelayModernRecord.toJSON<null | void>(record);
92
92
  }
@@ -47,6 +47,7 @@ function mark(
47
47
  references: DataIDSet,
48
48
  operationLoader: ?OperationLoader,
49
49
  shouldProcessClientComponents: ?boolean,
50
+ useExecTimeResolvers: ?boolean,
50
51
  ): void {
51
52
  const {dataID, node, variables} = selector;
52
53
  const marker = new RelayReferenceMarker(
@@ -55,6 +56,7 @@ function mark(
55
56
  references,
56
57
  operationLoader,
57
58
  shouldProcessClientComponents,
59
+ useExecTimeResolvers,
58
60
  );
59
61
  marker.mark(node, dataID);
60
62
  }
@@ -68,6 +70,7 @@ class RelayReferenceMarker {
68
70
  _recordSource: RecordSource;
69
71
  _references: DataIDSet;
70
72
  _variables: Variables;
73
+ _useExecTimeResolvers: boolean;
71
74
  _shouldProcessClientComponents: ?boolean;
72
75
 
73
76
  constructor(
@@ -76,9 +79,11 @@ class RelayReferenceMarker {
76
79
  references: DataIDSet,
77
80
  operationLoader: ?OperationLoader,
78
81
  shouldProcessClientComponents: ?boolean,
82
+ useExecTimeResolvers: ?boolean,
79
83
  ) {
80
84
  this._operationLoader = operationLoader ?? null;
81
85
  this._operationName = null;
86
+ this._useExecTimeResolvers = useExecTimeResolvers ?? false;
82
87
  this._recordSource = recordSource;
83
88
  this._references = references;
84
89
  this._variables = variables;
@@ -238,6 +243,10 @@ class RelayReferenceMarker {
238
243
  field: NormalizationClientEdgeToClientObject,
239
244
  record: Record,
240
245
  ): void {
246
+ if (this._useExecTimeResolvers) {
247
+ this._traverseLink(field.linkedField, record);
248
+ return;
249
+ }
241
250
  const dataID = this._traverseResolverField(field.backingField, record);
242
251
  if (dataID == null) {
243
252
  return;
@@ -290,6 +299,9 @@ class RelayReferenceMarker {
290
299
  field: NormalizationResolverField | NormalizationLiveResolverField,
291
300
  record: Record,
292
301
  ): ?DataID {
302
+ if (this._useExecTimeResolvers) {
303
+ return;
304
+ }
293
305
  const storageKey = getReadTimeResolverStorageKey(field, this._variables);
294
306
  const dataID = RelayModernRecord.getLinkedRecordID(record, storageKey);
295
307
 
@@ -305,7 +317,6 @@ class RelayReferenceMarker {
305
317
  // Mark the contents of the resolver's data dependencies.
306
318
  this._traverseSelections([fragment], record);
307
319
  }
308
-
309
320
  return dataID;
310
321
  }
311
322
 
@@ -67,7 +67,7 @@ const invariant = require('invariant');
67
67
  const warning = require('warning');
68
68
 
69
69
  export type GetDataID = (
70
- fieldValue: {[string]: mixed},
70
+ fieldValue: {+[string]: mixed},
71
71
  typeName: string,
72
72
  ) => mixed;
73
73
 
@@ -90,12 +90,14 @@ function normalize(
90
90
  response: PayloadData,
91
91
  options: NormalizationOptions,
92
92
  errors?: Array<PayloadError>,
93
+ useExecTimeResolvers?: boolean,
93
94
  ): RelayResponsePayload {
94
95
  const {dataID, node, variables} = selector;
95
96
  const normalizer = new RelayResponseNormalizer(
96
97
  recordSource,
97
98
  variables,
98
99
  options,
100
+ useExecTimeResolvers ?? false,
99
101
  );
100
102
  return normalizer.normalizeResponse(node, dataID, response, errors);
101
103
  }
@@ -117,6 +119,7 @@ class RelayResponseNormalizer {
117
119
  _path: Array<string>;
118
120
  _recordSource: MutableRecordSource;
119
121
  _variables: Variables;
122
+ _useExecTimeResolvers: boolean;
120
123
  _shouldProcessClientComponents: ?boolean;
121
124
  _errorTrie: RelayErrorTrie | null;
122
125
  _log: ?LogFunction;
@@ -125,6 +128,7 @@ class RelayResponseNormalizer {
125
128
  recordSource: MutableRecordSource,
126
129
  variables: Variables,
127
130
  options: NormalizationOptions,
131
+ useExecTimeResolvers: boolean,
128
132
  ) {
129
133
  this._actorIdentifier = options.actorIdentifier;
130
134
  this._getDataId = options.getDataID;
@@ -133,6 +137,7 @@ class RelayResponseNormalizer {
133
137
  this._incrementalPlaceholders = [];
134
138
  this._isClientExtension = false;
135
139
  this._isUnmatchedAbstractType = false;
140
+ this._useExecTimeResolvers = useExecTimeResolvers;
136
141
  this._followupPayloads = [];
137
142
  this._path = options.path ? [...options.path] : [];
138
143
  this._recordSource = recordSource;
@@ -325,10 +330,14 @@ class RelayResponseNormalizer {
325
330
  break;
326
331
  case 'RelayResolver':
327
332
  case 'RelayLiveResolver':
328
- this._normalizeResolver(selection, record, data);
333
+ if (!this._useExecTimeResolvers) {
334
+ this._normalizeResolver(selection, record, data);
335
+ }
329
336
  break;
330
337
  case 'ClientEdgeToClientObject':
331
- this._normalizeResolver(selection.backingField, record, data);
338
+ if (!this._useExecTimeResolvers) {
339
+ this._normalizeResolver(selection.backingField, record, data);
340
+ }
332
341
  break;
333
342
  default:
334
343
  (selection: empty);
@@ -245,7 +245,7 @@ export interface RecordSource {
245
245
  /**
246
246
  * A collection of records keyed by id.
247
247
  */
248
- export type RecordSourceJSON = {[DataID]: ?RecordJSON};
248
+ export type RecordSourceJSON = {+[DataID]: ?RecordJSON};
249
249
 
250
250
  /**
251
251
  * A read/write interface for accessing and updating graph data.
@@ -258,10 +258,10 @@ export interface MutableRecordSource extends RecordSource {
258
258
  }
259
259
 
260
260
  export type CheckOptions = {
261
- handlers: $ReadOnlyArray<MissingFieldHandler>,
262
- defaultActorIdentifier: ActorIdentifier,
263
- getTargetForActor: (actorIdentifier: ActorIdentifier) => MutableRecordSource,
264
- getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource,
261
+ +handlers: $ReadOnlyArray<MissingFieldHandler>,
262
+ +defaultActorIdentifier: ActorIdentifier,
263
+ +getTargetForActor: (actorIdentifier: ActorIdentifier) => MutableRecordSource,
264
+ +getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource,
265
265
  };
266
266
 
267
267
  export type OperationAvailability =
@@ -1172,6 +1172,7 @@ export type NormalizeResponseFunction = (
1172
1172
  selector: NormalizationSelector,
1173
1173
  typeName: string,
1174
1174
  options: NormalizationOptions,
1175
+ useExecTimeResolvers: boolean,
1175
1176
  ) => RelayResponsePayload;
1176
1177
 
1177
1178
  /**
@@ -32,11 +32,11 @@ export type EvaluationResult<T> = {
32
32
  error: ?Error,
33
33
  };
34
34
 
35
- export type ResolverFragmentResult = {
35
+ export type ResolverFragmentResult = $ReadOnly<{
36
36
  data: mixed,
37
37
  isMissingData: boolean,
38
38
  fieldErrors: ?FieldErrors,
39
- };
39
+ }>;
40
40
 
41
41
  export type GetDataForResolverFragmentFn =
42
42
  SingularReaderSelector => ResolverFragmentResult;
@@ -14,7 +14,7 @@
14
14
  const {VIEWER_ID, VIEWER_TYPE} = require('./ViewerPattern');
15
15
 
16
16
  function defaultGetDataID(
17
- fieldValue: {[string]: mixed},
17
+ fieldValue: {+[string]: mixed},
18
18
  typeName: string,
19
19
  ): mixed {
20
20
  if (typeName === VIEWER_TYPE) {
@@ -754,7 +754,8 @@ class LiveResolverCache implements ResolverCache {
754
754
  outputTypeDataID,
755
755
  variables,
756
756
  );
757
-
757
+ // LiveResolverCache is only used by read time resolvers, so this flag should be hardcoded as false.
758
+ const useExecTimeResolvers = false;
758
759
  const normalizationOptions =
759
760
  this._store.__getNormalizationOptions(fieldPath);
760
761
  // The resulted `source` is the normalized version of the
@@ -762,6 +763,7 @@ class LiveResolverCache implements ResolverCache {
762
763
  // All records in the `source` should have IDs that
763
764
  // is "prefix-ed" with the parent resolver record `ID`
764
765
  // and they don't expect to have a "strong" identifier.
766
+
765
767
  return normalize(
766
768
  source,
767
769
  selector,
@@ -771,6 +773,8 @@ class LiveResolverCache implements ResolverCache {
771
773
  // $FlowFixMe[incompatible-variance]
772
774
  value,
773
775
  normalizationOptions,
776
+ undefined,
777
+ useExecTimeResolvers,
774
778
  ).source;
775
779
  }
776
780
  // For weak models we have a simpler case. We simply need to update a
@@ -27,6 +27,7 @@ function normalizeResponse(
27
27
  selector: NormalizationSelector,
28
28
  typeName: string,
29
29
  options: NormalizationOptions,
30
+ useExecTimeResolvers: boolean,
30
31
  ): RelayResponsePayload {
31
32
  const {data, errors} = response;
32
33
  const source = RelayRecordSource.create();
@@ -38,6 +39,7 @@ function normalizeResponse(
38
39
  data,
39
40
  options,
40
41
  errors,
42
+ useExecTimeResolvers,
41
43
  );
42
44
  return {
43
45
  ...relayPayload,
@@ -41,16 +41,17 @@ export type SubscriptionParameters = {
41
41
  * Updated Flow type that makes use of typed graphql tagged literals with
42
42
  * type information.
43
43
  */
44
- export type GraphQLSubscriptionConfig<TVariables, TData, TRawResponse> = {
45
- configs?: Array<DeclarativeMutationConfig>,
46
- cacheConfig?: CacheConfig,
47
- subscription: GraphQLSubscription<TVariables, TData, TRawResponse>,
48
- variables: TVariables,
49
- onCompleted?: ?() => void,
50
- onError?: ?(error: Error) => void,
51
- onNext?: ?(response: ?TData) => void,
52
- updater?: ?SelectorStoreUpdater<TData>,
53
- };
44
+ export type GraphQLSubscriptionConfig<TVariables, TData, TRawResponse> =
45
+ $ReadOnly<{
46
+ configs?: Array<DeclarativeMutationConfig>,
47
+ cacheConfig?: CacheConfig,
48
+ subscription: GraphQLSubscription<TVariables, TData, TRawResponse>,
49
+ variables: TVariables,
50
+ onCompleted?: ?() => void,
51
+ onError?: ?(error: Error) => void,
52
+ onNext?: ?(response: ?TData) => void,
53
+ updater?: ?SelectorStoreUpdater<TData>,
54
+ }>;
54
55
 
55
56
  function requestSubscription<TVariables: Variables, TData, TRawResponse>(
56
57
  environment: IEnvironment,
@@ -71,6 +71,15 @@ export type FeatureFlags = {
71
71
 
72
72
  // Enable prefixing of DataID in the store with __typename
73
73
  ENABLE_TYPENAME_PREFIXED_DATA_ID: boolean,
74
+
75
+ // Relay previously had a bug where it would fail to check for missing client
76
+ // edge to server data in fragments nested within client edge Relay Resolver
77
+ // fields. This feature flag fixes the behavior but comes with a perf cost.
78
+ // This flag is here to allow us to gradually rollout the fix and track the perf
79
+ // impact.
80
+ //
81
+ // See https://github.com/facebook/relay/issues/4882
82
+ CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: boolean,
74
83
  };
75
84
 
76
85
  const RelayFeatureFlags: FeatureFlags = {
@@ -99,6 +108,7 @@ const RelayFeatureFlags: FeatureFlags = {
99
108
  DISALLOW_NESTED_UPDATES: false,
100
109
  ENABLE_TYPENAME_PREFIXED_DATA_ID: false,
101
110
  ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false,
111
+ CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false,
102
112
  };
103
113
 
104
114
  module.exports = RelayFeatureFlags;
@@ -62,14 +62,14 @@ export type VariablesOf<T: OperationType> = T['variables'];
62
62
  * a given instance of executing an operation.
63
63
  */
64
64
  export type CacheConfig = {
65
- force?: ?boolean,
66
- poll?: ?number,
67
- liveConfigId?: ?string,
68
- onSubscribe?: () => void,
69
- onResume?: (pauseTimeMs: number) => void,
70
- onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void,
71
- metadata?: {[key: string]: mixed, ...},
72
- transactionId?: ?string,
65
+ +force?: ?boolean,
66
+ +poll?: ?number,
67
+ +liveConfigId?: ?string,
68
+ +onSubscribe?: () => void,
69
+ +onResume?: (pauseTimeMs: number) => void,
70
+ +onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void,
71
+ +metadata?: {+[key: string]: mixed, ...},
72
+ +transactionId?: ?string,
73
73
  };
74
74
 
75
75
  export type FetchQueryFetchPolicy = 'store-or-network' | 'network-only';