relay-runtime 0.0.0-main-f16f5613 → 0.0.0-main-df676950

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.
@@ -15,6 +15,7 @@ import type {PayloadError} from '../network/RelayNetworkTypes';
15
15
 
16
16
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
17
17
 
18
+ // $FlowFixMe[recursive-definition]
18
19
  const SELF: Self = Symbol('$SELF');
19
20
 
20
21
  class RelayFieldError extends Error {
@@ -210,6 +210,7 @@ class RelayPublishQueue implements PublishQueue {
210
210
  sourceOperation?: OperationDescriptor,
211
211
  ): $ReadOnlyArray<RequestDescriptor> {
212
212
  const runWillClearGcHold =
213
+ // $FlowFixMe[incompatible-type]
213
214
  this._appliedOptimisticUpdates === 0 && !!this._gcHold;
214
215
  const runIsANoop =
215
216
  // this._pendingBackupRebase is true if an applied optimistic
@@ -353,12 +353,12 @@ class RelayReader {
353
353
  ) {
354
354
  const {to} = selection;
355
355
  const field = selection.field?.backingField ?? selection.field;
356
- const applicationName = field?.alias ?? field?.name;
356
+ const fieldName = field?.alias ?? field?.name;
357
357
 
358
358
  // ReaderClientExtension doesn't have `alias` or `name`
359
359
  // so we don't support this yet
360
360
  invariant(
361
- applicationName != null,
361
+ fieldName != null,
362
362
  "Couldn't determine field name for this field. It might be a ReaderClientExtension - which is not yet supported.",
363
363
  );
364
364
 
@@ -399,14 +399,14 @@ class RelayReader {
399
399
  if (this._errorResponseFields != null) {
400
400
  const errors = this._errorResponseFields.map(error => error.error);
401
401
 
402
- data[applicationName] = {
402
+ data[fieldName] = {
403
403
  ok: false,
404
404
  errors,
405
405
  };
406
406
  return;
407
407
  }
408
408
 
409
- data[applicationName] = {
409
+ data[fieldName] = {
410
410
  ok: true,
411
411
  value,
412
412
  };
@@ -662,8 +662,8 @@ class RelayReader {
662
662
  const parentRecordID = RelayModernRecord.getDataID(record);
663
663
  const result = this._readResolverFieldImpl(field, parentRecordID);
664
664
 
665
- const applicationName = field.alias ?? field.name;
666
- data[applicationName] = result;
665
+ const fieldName = field.alias ?? field.name;
666
+ data[fieldName] = result;
667
667
  return result;
668
668
  }
669
669
 
@@ -856,24 +856,24 @@ class RelayReader {
856
856
  const backingField = field.backingField;
857
857
 
858
858
  // Because ReaderClientExtension doesn't have `alias` or `name` and so I don't know
859
- // how to get its applicationName or storageKey yet:
859
+ // how to get its fieldName or storageKey yet:
860
860
  invariant(
861
861
  backingField.kind !== 'ClientExtension',
862
862
  'Client extension client edges are not yet implemented.',
863
863
  );
864
864
 
865
- const applicationName = backingField.alias ?? backingField.name;
865
+ const fieldName = backingField.alias ?? backingField.name;
866
866
  const backingFieldData = {};
867
867
  this._traverseSelections([backingField], record, backingFieldData);
868
- // At this point, backingFieldData is an object with a single key (applicationName)
868
+ // At this point, backingFieldData is an object with a single key (fieldName)
869
869
  // whose value is the value returned from the resolver, or a suspense sentinel.
870
870
 
871
- const clientEdgeResolverResponse = backingFieldData[applicationName];
871
+ const clientEdgeResolverResponse = backingFieldData[fieldName];
872
872
  if (
873
873
  clientEdgeResolverResponse == null ||
874
874
  isSuspenseSentinel(clientEdgeResolverResponse)
875
875
  ) {
876
- data[applicationName] = clientEdgeResolverResponse;
876
+ data[fieldName] = clientEdgeResolverResponse;
877
877
  return clientEdgeResolverResponse;
878
878
  }
879
879
 
@@ -925,7 +925,7 @@ class RelayReader {
925
925
  data,
926
926
  );
927
927
  this._clientEdgeTraversalPath.pop();
928
- data[applicationName] = edgeValues;
928
+ data[fieldName] = edgeValues;
929
929
  return edgeValues;
930
930
  } else {
931
931
  const id = extractIdFromResponse(clientEdgeResolverResponse);
@@ -970,18 +970,18 @@ class RelayReader {
970
970
  if (model == null) {
971
971
  // If the model resolver returns undefined, we should still return null
972
972
  // to match GQL behavior.
973
- data[applicationName] = null;
973
+ data[fieldName] = null;
974
974
  return null;
975
975
  }
976
976
  }
977
977
  this._clientEdgeTraversalPath.push(traversalPathSegment);
978
978
 
979
- const prevData = data[applicationName];
979
+ const prevData = data[fieldName];
980
980
  invariant(
981
981
  prevData == null || typeof prevData === 'object',
982
982
  'RelayReader(): Expected data for field `%s` on record `%s` ' +
983
983
  'to be an object, got `%s`.',
984
- applicationName,
984
+ fieldName,
985
985
  RelayModernRecord.getDataID(record),
986
986
  prevData,
987
987
  );
@@ -992,7 +992,7 @@ class RelayReader {
992
992
  prevData,
993
993
  );
994
994
  this._clientEdgeTraversalPath.pop();
995
- data[applicationName] = edgeValue;
995
+ data[fieldName] = edgeValue;
996
996
  return edgeValue;
997
997
  }
998
998
  }
@@ -1002,7 +1002,7 @@ class RelayReader {
1002
1002
  record: Record,
1003
1003
  data: SelectorData,
1004
1004
  ): ?mixed {
1005
- const applicationName = field.alias ?? field.name;
1005
+ const fieldName = field.alias ?? field.name;
1006
1006
  const storageKey = getStorageKey(field, this._variables);
1007
1007
  const value = RelayModernRecord.getValue(record, storageKey);
1008
1008
  if (value === null) {
@@ -1010,7 +1010,7 @@ class RelayReader {
1010
1010
  } else if (value === undefined) {
1011
1011
  this._markDataAsMissing();
1012
1012
  }
1013
- data[applicationName] = value;
1013
+ data[fieldName] = value;
1014
1014
  return value;
1015
1015
  }
1016
1016
 
@@ -1019,11 +1019,11 @@ class RelayReader {
1019
1019
  record: Record,
1020
1020
  data: SelectorData,
1021
1021
  ): ?mixed {
1022
- const applicationName = field.alias ?? field.name;
1022
+ const fieldName = field.alias ?? field.name;
1023
1023
  const storageKey = getStorageKey(field, this._variables);
1024
1024
  const linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
1025
1025
  if (linkedID == null) {
1026
- data[applicationName] = linkedID;
1026
+ data[fieldName] = linkedID;
1027
1027
  if (linkedID === null) {
1028
1028
  this._maybeAddErrorResponseFields(record, storageKey);
1029
1029
  } else if (linkedID === undefined) {
@@ -1032,18 +1032,18 @@ class RelayReader {
1032
1032
  return linkedID;
1033
1033
  }
1034
1034
 
1035
- const prevData = data[applicationName];
1035
+ const prevData = data[fieldName];
1036
1036
  invariant(
1037
1037
  prevData == null || typeof prevData === 'object',
1038
1038
  'RelayReader(): Expected data for field `%s` on record `%s` ' +
1039
1039
  'to be an object, got `%s`.',
1040
- applicationName,
1040
+ fieldName,
1041
1041
  RelayModernRecord.getDataID(record),
1042
1042
  prevData,
1043
1043
  );
1044
1044
  // $FlowFixMe[incompatible-variance]
1045
1045
  const value = this._traverse(field, linkedID, prevData);
1046
- data[applicationName] = value;
1046
+ data[fieldName] = value;
1047
1047
  return value;
1048
1048
  }
1049
1049
 
@@ -1052,7 +1052,7 @@ class RelayReader {
1052
1052
  record: Record,
1053
1053
  data: SelectorData,
1054
1054
  ): ?mixed {
1055
- const applicationName = field.alias ?? field.name;
1055
+ const fieldName = field.alias ?? field.name;
1056
1056
  const storageKey = getStorageKey(field, this._variables);
1057
1057
  const externalRef = RelayModernRecord.getActorLinkedRecordID(
1058
1058
  record,
@@ -1060,13 +1060,13 @@ class RelayReader {
1060
1060
  );
1061
1061
 
1062
1062
  if (externalRef == null) {
1063
- data[applicationName] = externalRef;
1063
+ data[fieldName] = externalRef;
1064
1064
  if (externalRef === undefined) {
1065
1065
  this._markDataAsMissing();
1066
1066
  } else if (externalRef === null) {
1067
1067
  this._maybeAddErrorResponseFields(record, storageKey);
1068
1068
  }
1069
- return data[applicationName];
1069
+ return data[fieldName];
1070
1070
  }
1071
1071
  const [actorIdentifier, dataID] = externalRef;
1072
1072
 
@@ -1078,11 +1078,11 @@ class RelayReader {
1078
1078
  }),
1079
1079
  fragmentRef,
1080
1080
  );
1081
- data[applicationName] = {
1081
+ data[fieldName] = {
1082
1082
  __fragmentRef: fragmentRef,
1083
1083
  __viewer: actorIdentifier,
1084
1084
  };
1085
- return data[applicationName];
1085
+ return data[fieldName];
1086
1086
  }
1087
1087
 
1088
1088
  _readPluralLink(
@@ -1104,22 +1104,22 @@ class RelayReader {
1104
1104
  record: Record,
1105
1105
  data: SelectorData,
1106
1106
  ): ?mixed {
1107
- const applicationName = field.alias ?? field.name;
1107
+ const fieldName = field.alias ?? field.name;
1108
1108
 
1109
1109
  if (linkedIDs == null) {
1110
- data[applicationName] = linkedIDs;
1110
+ data[fieldName] = linkedIDs;
1111
1111
  if (linkedIDs === undefined) {
1112
1112
  this._markDataAsMissing();
1113
1113
  }
1114
1114
  return linkedIDs;
1115
1115
  }
1116
1116
 
1117
- const prevData = data[applicationName];
1117
+ const prevData = data[fieldName];
1118
1118
  invariant(
1119
1119
  prevData == null || Array.isArray(prevData),
1120
1120
  'RelayReader(): Expected data for field `%s` on record `%s` ' +
1121
1121
  'to be an array, got `%s`.',
1122
- applicationName,
1122
+ fieldName,
1123
1123
  RelayModernRecord.getDataID(record),
1124
1124
  prevData,
1125
1125
  );
@@ -1138,7 +1138,7 @@ class RelayReader {
1138
1138
  prevItem == null || typeof prevItem === 'object',
1139
1139
  'RelayReader(): Expected data for field `%s` on record `%s` ' +
1140
1140
  'to be an object, got `%s`.',
1141
- applicationName,
1141
+ fieldName,
1142
1142
  RelayModernRecord.getDataID(record),
1143
1143
  prevItem,
1144
1144
  );
@@ -1146,7 +1146,7 @@ class RelayReader {
1146
1146
  // $FlowFixMe[incompatible-variance]
1147
1147
  linkedArray[nextIndex] = this._traverse(field, linkedID, prevItem);
1148
1148
  });
1149
- data[applicationName] = linkedArray;
1149
+ data[fieldName] = linkedArray;
1150
1150
  return linkedArray;
1151
1151
  }
1152
1152
 
@@ -26,6 +26,7 @@ import type {
26
26
 
27
27
  const recycleNodesInto = require('../util/recycleNodesInto');
28
28
  const {RELAY_LIVE_RESOLVER} = require('../util/RelayConcreteNode');
29
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
29
30
  const shallowFreeze = require('../util/shallowFreeze');
30
31
  const {generateClientID} = require('./ClientID');
31
32
  const RelayModernRecord = require('./RelayModernRecord');
@@ -325,6 +326,22 @@ class RecordResolverCache implements ResolverCache {
325
326
  if (recycled !== originalInputs) {
326
327
  return true;
327
328
  }
329
+
330
+ if (RelayFeatureFlags.MARK_RESOLVER_VALUES_AS_CLEAN_AFTER_FRAGMENT_REREAD) {
331
+ // This record does not need to be recomputed, we can reuse the cached value.
332
+ // For subsequent reads we can mark this record as "clean" so that they will
333
+ // not need to re-read the fragment.
334
+ const nextRecord = RelayModernRecord.clone(record);
335
+ RelayModernRecord.setValue(
336
+ nextRecord,
337
+ RELAY_RESOLVER_INVALIDATION_KEY,
338
+ false,
339
+ );
340
+
341
+ const recordSource = this._getRecordSource();
342
+ recordSource.set(RelayModernRecord.getDataID(record), nextRecord);
343
+ }
344
+
328
345
  return false;
329
346
  }
330
347
 
@@ -35,6 +35,7 @@ import type {LiveState} from 'relay-runtime';
35
35
 
36
36
  const recycleNodesInto = require('../../util/recycleNodesInto');
37
37
  const {RELAY_LIVE_RESOLVER} = require('../../util/RelayConcreteNode');
38
+ const RelayFeatureFlags = require('../../util/RelayFeatureFlags');
38
39
  const shallowFreeze = require('../../util/shallowFreeze');
39
40
  const {generateClientID, generateClientObjectClientID} = require('../ClientID');
40
41
  const RelayModernRecord = require('../RelayModernRecord');
@@ -699,6 +700,22 @@ class LiveResolverCache implements ResolverCache {
699
700
  if (recycled !== originalInputs) {
700
701
  return true;
701
702
  }
703
+
704
+ if (RelayFeatureFlags.MARK_RESOLVER_VALUES_AS_CLEAN_AFTER_FRAGMENT_REREAD) {
705
+ // This record does not need to be recomputed, we can reuse the cached value.
706
+ // For subsequent reads we can mark this record as "clean" so that they will
707
+ // not need to re-read the fragment.
708
+ const nextRecord = RelayModernRecord.clone(record);
709
+ RelayModernRecord.setValue(
710
+ nextRecord,
711
+ RELAY_RESOLVER_INVALIDATION_KEY,
712
+ false,
713
+ );
714
+
715
+ const recordSource = this._getRecordSource();
716
+ recordSource.set(RelayModernRecord.getDataID(record), nextRecord);
717
+ }
718
+
702
719
  return false;
703
720
  }
704
721
 
@@ -48,6 +48,9 @@ export type FeatureFlags = {
48
48
  ENABLE_FIELD_ERROR_HANDLING_CATCH_DIRECTIVE: boolean,
49
49
 
50
50
  PROCESS_OPTIMISTIC_UPDATE_BEFORE_SUBSCRIPTION: boolean,
51
+
52
+ // Temporary flag to enable a gradual rollout of the fix for T185969900
53
+ MARK_RESOLVER_VALUES_AS_CLEAN_AFTER_FRAGMENT_REREAD: boolean,
51
54
  };
52
55
 
53
56
  const RelayFeatureFlags: FeatureFlags = {
@@ -71,6 +74,7 @@ const RelayFeatureFlags: FeatureFlags = {
71
74
  ENABLE_FIELD_ERROR_HANDLING_THROW_BY_DEFAULT: false,
72
75
  ENABLE_FIELD_ERROR_HANDLING_CATCH_DIRECTIVE: false,
73
76
  PROCESS_OPTIMISTIC_UPDATE_BEFORE_SUBSCRIPTION: false,
77
+ MARK_RESOLVER_VALUES_AS_CLEAN_AFTER_FRAGMENT_REREAD: false,
74
78
  };
75
79
 
76
80
  module.exports = RelayFeatureFlags;