relay-runtime 0.0.0-main-6cd701c5 → 0.0.0-main-e7cacf79

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 v0.0.0-main-6cd701c5
2
+ * Relay v0.0.0-main-e7cacf79
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 v0.0.0-main-6cd701c5
2
+ * Relay v0.0.0-main-e7cacf79
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
@@ -14,7 +14,7 @@ var RelayRecordSource = require('../store/RelayRecordSource');
14
14
  var ActorSpecificEnvironment = require('./ActorSpecificEnvironment');
15
15
  var MultiActorEnvironment = /*#__PURE__*/function () {
16
16
  function MultiActorEnvironment(config) {
17
- var _config$getDataID, _config$logFn, _config$relayFieldLog, _config$treatMissingF, _config$isServer, _config$missingFieldH, _config$defaultRender, _config$normalizeResp;
17
+ var _config$getDataID, _config$logFn, _config$relayFieldLog, _config$treatMissingF, _config$deferDeduplic, _config$isServer, _config$missingFieldH, _config$defaultRender, _config$normalizeResp;
18
18
  this._actorEnvironments = new Map();
19
19
  this._operationLoader = config.operationLoader;
20
20
  this._createNetworkForActor = config.createNetworkForActor;
@@ -26,6 +26,7 @@ var MultiActorEnvironment = /*#__PURE__*/function () {
26
26
  this._relayFieldLogger = (_config$relayFieldLog = config.relayFieldLogger) !== null && _config$relayFieldLog !== void 0 ? _config$relayFieldLog : defaultRelayFieldLogger;
27
27
  this._shouldProcessClientComponents = config.shouldProcessClientComponents;
28
28
  this._treatMissingFieldsAsNull = (_config$treatMissingF = config.treatMissingFieldsAsNull) !== null && _config$treatMissingF !== void 0 ? _config$treatMissingF : false;
29
+ this._deferDeduplicatedFields = (_config$deferDeduplic = config.deferDeduplicatedFields) !== null && _config$deferDeduplic !== void 0 ? _config$deferDeduplic : false;
29
30
  this._isServer = (_config$isServer = config.isServer) !== null && _config$isServer !== void 0 ? _config$isServer : false;
30
31
  this._missingFieldHandlers = (_config$missingFieldH = config.missingFieldHandlers) !== null && _config$missingFieldH !== void 0 ? _config$missingFieldH : [];
31
32
  this._createStoreForActor = config.createStoreForActor;
@@ -288,6 +289,7 @@ var MultiActorEnvironment = /*#__PURE__*/function () {
288
289
  return _this4.forActor(actorIdentifier).getStore();
289
290
  },
290
291
  treatMissingFieldsAsNull: _this4._treatMissingFieldsAsNull,
292
+ deferDeduplicatedFields: _this4._deferDeduplicatedFields,
291
293
  updater: updater,
292
294
  log: _this4._logFn,
293
295
  normalizeResponse: _this4._normalizeResponse
@@ -49,12 +49,14 @@ var Executor = /*#__PURE__*/function () {
49
49
  sink = _ref3.sink,
50
50
  source = _ref3.source,
51
51
  treatMissingFieldsAsNull = _ref3.treatMissingFieldsAsNull,
52
+ deferDeduplicatedFields = _ref3.deferDeduplicatedFields,
52
53
  updater = _ref3.updater,
53
54
  log = _ref3.log,
54
55
  normalizeResponse = _ref3.normalizeResponse;
55
56
  this._actorIdentifier = actorIdentifier;
56
57
  this._getDataID = getDataID;
57
58
  this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
59
+ this._deferDeduplicatedFields = deferDeduplicatedFields;
58
60
  this._incrementalPayloadsPending = false;
59
61
  this._incrementalResults = new Map();
60
62
  this._log = log;
@@ -435,6 +437,7 @@ var Executor = /*#__PURE__*/function () {
435
437
  if (response) {
436
438
  var payload = this._normalizeResponse(response, this._operation.root, ROOT_TYPE, {
437
439
  actorIdentifier: this._actorIdentifier,
440
+ deferDeduplicatedFields: false,
438
441
  getDataID: this._getDataID,
439
442
  log: this._log,
440
443
  path: [],
@@ -521,6 +524,7 @@ var Executor = /*#__PURE__*/function () {
521
524
  };
522
525
  return this._normalizeResponse(nextResponse, selector, followupPayload.typeName, {
523
526
  actorIdentifier: this._actorIdentifier,
527
+ deferDeduplicatedFields: false,
524
528
  getDataID: this._getDataID,
525
529
  log: this._log,
526
530
  path: followupPayload.path,
@@ -578,6 +582,7 @@ var Executor = /*#__PURE__*/function () {
578
582
  return responses.map(function (payloadPart) {
579
583
  var relayPayload = _this8._normalizeResponse(payloadPart, _this8._operation.root, ROOT_TYPE, {
580
584
  actorIdentifier: _this8._actorIdentifier,
585
+ deferDeduplicatedFields: false,
581
586
  getDataID: _this8._getDataID,
582
587
  log: _this8._log,
583
588
  path: [],
@@ -868,6 +873,7 @@ var Executor = /*#__PURE__*/function () {
868
873
  this._actorIdentifier = (_placeholder$actorIde = placeholder.actorIdentifier) !== null && _placeholder$actorIde !== void 0 ? _placeholder$actorIde : this._actorIdentifier;
869
874
  var relayPayload = this._normalizeResponse(response, placeholder.selector, placeholder.typeName, {
870
875
  actorIdentifier: this._actorIdentifier,
876
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
871
877
  getDataID: this._getDataID,
872
878
  log: this._log,
873
879
  path: placeholder.path,
@@ -971,6 +977,7 @@ var Executor = /*#__PURE__*/function () {
971
977
  });
972
978
  var relayPayload = this._normalizeResponse(response, selector, typeName, {
973
979
  actorIdentifier: this._actorIdentifier,
980
+ deferDeduplicatedFields: false,
974
981
  getDataID: this._getDataID,
975
982
  log: this._log,
976
983
  path: [].concat((0, _toConsumableArray2["default"])(normalizationPath), [responseKey, String(itemIndex)]),
@@ -24,6 +24,7 @@ var RelayModernEnvironment = /*#__PURE__*/function () {
24
24
  var _config$store, _config$log, _config$relayFieldLog, _config$UNSTABLE_defa, _config$getDataID, _config$missingFieldH, _config$handlerProvid, _config$scheduler, _config$isServer, _config$normalizeResp, _config$operationTrac;
25
25
  this.configName = config.configName;
26
26
  this._treatMissingFieldsAsNull = config.treatMissingFieldsAsNull === true;
27
+ this._deferDeduplicatedFields = config.deferDeduplicatedFields === true;
27
28
  var operationLoader = config.operationLoader;
28
29
  if (process.env.NODE_ENV !== "production") {
29
30
  if (operationLoader != null) {
@@ -308,6 +309,7 @@ var RelayModernEnvironment = /*#__PURE__*/function () {
308
309
  sink: sink,
309
310
  source: createSource(),
310
311
  treatMissingFieldsAsNull: _this0._treatMissingFieldsAsNull,
312
+ deferDeduplicatedFields: _this0._deferDeduplicatedFields,
311
313
  updater: updater
312
314
  });
313
315
  return function () {
@@ -29,7 +29,7 @@ var DEFAULT_RELEASE_BUFFER_SIZE = 10;
29
29
  var RelayModernStore = /*#__PURE__*/function () {
30
30
  function RelayModernStore(source, options) {
31
31
  var _this = this;
32
- var _options$gcReleaseBuf, _options$shouldRetain, _options$gcScheduler, _options$getDataID, _options$log, _options$operationLoa, _options$shouldProces, _options$treatMissing;
32
+ var _options$gcReleaseBuf, _options$shouldRetain, _options$gcScheduler, _options$getDataID, _options$log, _options$operationLoa, _options$shouldProces, _options$treatMissing, _options$deferDedupli;
33
33
  (0, _defineProperty2["default"])(this, "_gcStep", function () {
34
34
  if (_this._gcRun) {
35
35
  if (_this._gcRun.next().done) {
@@ -74,6 +74,7 @@ var RelayModernStore = /*#__PURE__*/function () {
74
74
  this._updatedRecordIDs = new Set();
75
75
  this._shouldProcessClientComponents = (_options$shouldProces = options === null || options === void 0 ? void 0 : options.shouldProcessClientComponents) !== null && _options$shouldProces !== void 0 ? _options$shouldProces : false;
76
76
  this._treatMissingFieldsAsNull = (_options$treatMissing = options === null || options === void 0 ? void 0 : options.treatMissingFieldsAsNull) !== null && _options$treatMissing !== void 0 ? _options$treatMissing : false;
77
+ this._deferDeduplicatedFields = (_options$deferDedupli = options === null || options === void 0 ? void 0 : options.deferDeduplicatedFields) !== null && _options$deferDedupli !== void 0 ? _options$deferDedupli : false;
77
78
  this._actorIdentifier = options === null || options === void 0 ? void 0 : options.actorIdentifier;
78
79
  initializeRecordSource(this._recordSource);
79
80
  }
@@ -216,7 +217,7 @@ var RelayModernStore = /*#__PURE__*/function () {
216
217
  });
217
218
  }
218
219
  var source = this.getSource();
219
- var snapshot = RelayReader.read(source, selector, this._resolverCache, this._resolverContext);
220
+ var snapshot = RelayReader.read(source, selector, log, this._resolverCache, this._resolverContext);
220
221
  if (process.env.NODE_ENV !== "production") {
221
222
  deepFreeze(snapshot);
222
223
  }
@@ -537,6 +538,7 @@ var RelayModernStore = /*#__PURE__*/function () {
537
538
  getDataID: this._getDataID,
538
539
  log: this.__log,
539
540
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
541
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
540
542
  shouldProcessClientComponents: this._shouldProcessClientComponents,
541
543
  actorIdentifier: this._actorIdentifier
542
544
  };
@@ -203,7 +203,7 @@ var RelayPublishQueue = /*#__PURE__*/function () {
203
203
  return RelayPublishQueue;
204
204
  }();
205
205
  function lookupSelector(source, selector) {
206
- var selectorData = RelayReader.read(source, selector).data;
206
+ var selectorData = RelayReader.read(source, selector, null, undefined, undefined).data;
207
207
  if (process.env.NODE_ENV !== "production") {
208
208
  var deepFreeze = require('../util/deepFreeze');
209
209
  if (selectorData) {
@@ -30,12 +30,12 @@ var _require4 = require('./ResolverFragments'),
30
30
  var _require5 = require('./TypeID'),
31
31
  generateTypeID = _require5.generateTypeID;
32
32
  var invariant = require('invariant');
33
- function read(recordSource, selector, resolverCache, resolverContext) {
34
- var reader = new RelayReader(recordSource, selector, resolverCache !== null && resolverCache !== void 0 ? resolverCache : new NoopResolverCache(), resolverContext);
33
+ function read(recordSource, selector, log, resolverCache, resolverContext) {
34
+ var reader = new RelayReader(recordSource, selector, log, resolverCache !== null && resolverCache !== void 0 ? resolverCache : new NoopResolverCache(), resolverContext);
35
35
  return reader.read();
36
36
  }
37
37
  var RelayReader = /*#__PURE__*/function () {
38
- function RelayReader(recordSource, selector, resolverCache, resolverContext) {
38
+ function RelayReader(recordSource, selector, log, resolverCache, resolverContext) {
39
39
  var _selector$clientEdgeT, _ref, _this$_owner$node$ope, _this$_owner$node$ope2;
40
40
  this._clientEdgeTraversalPath = (_selector$clientEdgeT = selector.clientEdgeTraversalPath) !== null && _selector$clientEdgeT !== void 0 && _selector$clientEdgeT.length ? (0, _toConsumableArray2["default"])(selector.clientEdgeTraversalPath) : [];
41
41
  this._missingClientEdges = [];
@@ -53,6 +53,7 @@ var RelayReader = /*#__PURE__*/function () {
53
53
  this._fragmentName = selector.node.name;
54
54
  this._updatedDataIDs = new Set();
55
55
  this._resolverContext = resolverContext;
56
+ this._log = log;
56
57
  }
57
58
  var _proto = RelayReader.prototype;
58
59
  _proto.read = function read() {
@@ -85,6 +86,13 @@ var RelayReader = /*#__PURE__*/function () {
85
86
  this._resolverCache.notifyUpdatedSubscribers(this._updatedDataIDs);
86
87
  this._updatedDataIDs.clear();
87
88
  }
89
+ if (RelayFeatureFlags.ENABLE_READER_FRAGMENTS_LOGGING) {
90
+ var _this$_log;
91
+ (_this$_log = this._log) === null || _this$_log === void 0 ? void 0 : _this$_log.call(this, {
92
+ name: 'reader.read',
93
+ selector: this._selector
94
+ });
95
+ }
88
96
  return {
89
97
  data: data,
90
98
  fieldErrors: this._fieldErrors,
@@ -456,7 +464,7 @@ var RelayReader = /*#__PURE__*/function () {
456
464
  isMissingData: snapshot.isMissingData
457
465
  };
458
466
  }
459
- snapshot = read(_this._recordSource, singularReaderSelector, _this._resolverCache);
467
+ snapshot = read(_this._recordSource, singularReaderSelector, null, _this._resolverCache, undefined);
460
468
  return {
461
469
  data: snapshot.data,
462
470
  fieldErrors: snapshot.fieldErrors,
@@ -860,11 +868,20 @@ var RelayReader = /*#__PURE__*/function () {
860
868
  if (data[ID_KEY] == null) {
861
869
  data[ID_KEY] = RelayModernRecord.getDataID(record);
862
870
  }
863
- fragmentPointers[fragmentSpread.name] = getArgumentValues(fragmentSpread.args, this._variables, this._isWithinUnmatchedTypeRefinement);
871
+ var args = getArgumentValues(fragmentSpread.args, this._variables, this._isWithinUnmatchedTypeRefinement);
872
+ fragmentPointers[fragmentSpread.name] = args;
864
873
  data[FRAGMENT_OWNER_KEY] = this._owner;
865
874
  if (this._clientEdgeTraversalPath.length > 0 && this._clientEdgeTraversalPath[this._clientEdgeTraversalPath.length - 1] !== null) {
866
875
  data[CLIENT_EDGE_TRAVERSAL_PATH] = (0, _toConsumableArray2["default"])(this._clientEdgeTraversalPath);
867
876
  }
877
+ if (RelayFeatureFlags.ENABLE_READER_FRAGMENTS_LOGGING) {
878
+ var _this$_log2;
879
+ (_this$_log2 = this._log) === null || _this$_log2 === void 0 ? void 0 : _this$_log2.call(this, {
880
+ name: 'reader.fragmentSpread',
881
+ fragmentName: fragmentSpread.name,
882
+ data: data
883
+ });
884
+ }
868
885
  };
869
886
  _proto._createInlineDataOrResolverFragmentPointer = function _createInlineDataOrResolverFragmentPointer(fragmentSpreadOrFragment, record, data) {
870
887
  var fragmentPointers = data[FRAGMENTS_KEY];
@@ -47,6 +47,7 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
47
47
  this._getDataId = options.getDataID;
48
48
  this._handleFieldPayloads = [];
49
49
  this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
50
+ this._deferDeduplicatedFields = options.deferDeduplicatedFields;
50
51
  this._incrementalPlaceholders = [];
51
52
  this._isClientExtension = false;
52
53
  this._isUnmatchedAbstractType = false;
@@ -300,7 +301,7 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
300
301
  var isNoncompliantlyNullish = RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS && Array.isArray(fieldValue) && fieldValue.length === 0;
301
302
  if (fieldValue == null || isNoncompliantlyNullish) {
302
303
  if (fieldValue === undefined) {
303
- var isOptionalField = this._isClientExtension || this._isUnmatchedAbstractType;
304
+ var isOptionalField = this._isClientExtension || this._isUnmatchedAbstractType || this._deferDeduplicatedFields;
304
305
  if (isOptionalField) {
305
306
  return;
306
307
  } else if (!this._treatMissingFieldsAsNull) {
@@ -42,7 +42,7 @@ var RelayStoreSubscriptions = /*#__PURE__*/function () {
42
42
  return;
43
43
  }
44
44
  var snapshot = subscription.snapshot;
45
- var backup = RelayReader.read(source, snapshot.selector, _this2._resolverCache, _this2._resolverContext);
45
+ var backup = RelayReader.read(source, snapshot.selector, _this2.__log, _this2._resolverCache, _this2._resolverContext);
46
46
  var nextData = recycleNodesInto(snapshot.data, backup.data);
47
47
  backup.data = nextData;
48
48
  subscription.backup = backup;
@@ -106,7 +106,7 @@ var RelayStoreSubscriptions = /*#__PURE__*/function () {
106
106
  if (!stale && !hasOverlappingUpdates) {
107
107
  return;
108
108
  }
109
- var nextSnapshot = hasOverlappingUpdates || !backup ? RelayReader.read(source, snapshot.selector, this._resolverCache, this._resolverContext) : backup;
109
+ var nextSnapshot = hasOverlappingUpdates || !backup ? RelayReader.read(source, snapshot.selector, this.__log, this._resolverCache, this._resolverContext) : backup;
110
110
  var nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
111
111
  nextSnapshot = {
112
112
  data: nextData,
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  function createError(type, name, messageFormat) {
4
+ var _messageParams$;
4
5
  for (var _len = arguments.length, messageParams = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
5
6
  messageParams[_key - 3] = arguments[_key];
6
7
  }
@@ -9,11 +10,13 @@ function createError(type, name, messageFormat) {
9
10
  return String(messageParams[index++]);
10
11
  });
11
12
  var err = new Error(message);
13
+ var operation = (_messageParams$ = messageParams[2]) !== null && _messageParams$ !== void 0 ? _messageParams$ : null;
12
14
  var error = Object.assign(err, {
13
15
  name: name,
14
16
  messageFormat: messageFormat,
15
17
  messageParams: messageParams,
16
18
  type: type,
19
+ operation: operation,
17
20
  taalOpcodes: [2, 2]
18
21
  });
19
22
  if (error.stack === undefined) {
@@ -28,6 +28,7 @@ var RelayFeatureFlags = {
28
28
  ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false,
29
29
  CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false,
30
30
  FILTER_OUT_RELAY_RESOLVER_RECORDS: false,
31
- OPTIMIZE_NOTIFY: false
31
+ OPTIMIZE_NOTIFY: false,
32
+ ENABLE_READER_FRAGMENTS_LOGGING: false
32
33
  };
33
34
  module.exports = RelayFeatureFlags;
@@ -71,6 +71,7 @@ export type MultiActorEnvironmentConfig = $ReadOnly<{
71
71
  scheduler?: ?TaskScheduler,
72
72
  shouldProcessClientComponents?: ?boolean,
73
73
  treatMissingFieldsAsNull?: boolean,
74
+ deferDeduplicatedFields?: boolean,
74
75
  }>;
75
76
 
76
77
  class MultiActorEnvironment implements IMultiActorEnvironment {
@@ -91,6 +92,7 @@ class MultiActorEnvironment implements IMultiActorEnvironment {
91
92
  +_scheduler: ?TaskScheduler;
92
93
  +_shouldProcessClientComponents: ?boolean;
93
94
  +_treatMissingFieldsAsNull: boolean;
95
+ +_deferDeduplicatedFields: boolean;
94
96
 
95
97
  constructor(config: MultiActorEnvironmentConfig) {
96
98
  this._actorEnvironments = new Map();
@@ -106,6 +108,7 @@ class MultiActorEnvironment implements IMultiActorEnvironment {
106
108
  this._relayFieldLogger = config.relayFieldLogger ?? defaultRelayFieldLogger;
107
109
  this._shouldProcessClientComponents = config.shouldProcessClientComponents;
108
110
  this._treatMissingFieldsAsNull = config.treatMissingFieldsAsNull ?? false;
111
+ this._deferDeduplicatedFields = config.deferDeduplicatedFields ?? false;
109
112
  this._isServer = config.isServer ?? false;
110
113
  this._missingFieldHandlers = config.missingFieldHandlers ?? [];
111
114
  this._createStoreForActor = config.createStoreForActor;
@@ -471,6 +474,7 @@ class MultiActorEnvironment implements IMultiActorEnvironment {
471
474
  return this.forActor(actorIdentifier).getStore();
472
475
  },
473
476
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
477
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
474
478
  updater,
475
479
  log: this._logFn,
476
480
  normalizeResponse: this._normalizeResponse,
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": "0.0.0-main-6cd701c5",
4
+ "version": "0.0.0-main-e7cacf79",
5
5
  "keywords": [
6
6
  "graphql",
7
7
  "relay"
@@ -88,6 +88,7 @@ export type ExecuteConfig<TMutation: MutationParameters> = {
88
88
  +sink: Sink<GraphQLResponse>,
89
89
  +source: RelayObservable<GraphQLResponse>,
90
90
  +treatMissingFieldsAsNull: boolean,
91
+ +deferDeduplicatedFields: boolean,
91
92
  +updater?: ?SelectorStoreUpdater<TMutation['response']>,
92
93
  +log: LogFunction,
93
94
  };
@@ -127,6 +128,7 @@ class Executor<TMutation: MutationParameters> {
127
128
  _actorIdentifier: ActorIdentifier;
128
129
  _getDataID: GetDataID;
129
130
  _treatMissingFieldsAsNull: boolean;
131
+ _deferDeduplicatedFields: boolean;
130
132
  _incrementalPayloadsPending: boolean;
131
133
  _incrementalResults: Map<Label, Map<PathKey, IncrementalResults>>;
132
134
  _log: LogFunction;
@@ -178,6 +180,7 @@ class Executor<TMutation: MutationParameters> {
178
180
  sink,
179
181
  source,
180
182
  treatMissingFieldsAsNull,
183
+ deferDeduplicatedFields,
181
184
  updater,
182
185
  log,
183
186
  normalizeResponse,
@@ -185,6 +188,7 @@ class Executor<TMutation: MutationParameters> {
185
188
  this._actorIdentifier = actorIdentifier;
186
189
  this._getDataID = getDataID;
187
190
  this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
191
+ this._deferDeduplicatedFields = deferDeduplicatedFields;
188
192
  this._incrementalPayloadsPending = false;
189
193
  this._incrementalResults = new Map();
190
194
  this._log = log;
@@ -695,6 +699,7 @@ class Executor<TMutation: MutationParameters> {
695
699
  ROOT_TYPE,
696
700
  {
697
701
  actorIdentifier: this._actorIdentifier,
702
+ deferDeduplicatedFields: false,
698
703
  getDataID: this._getDataID,
699
704
  log: this._log,
700
705
  path: [],
@@ -813,6 +818,7 @@ class Executor<TMutation: MutationParameters> {
813
818
  followupPayload.typeName,
814
819
  {
815
820
  actorIdentifier: this._actorIdentifier,
821
+ deferDeduplicatedFields: false,
816
822
  getDataID: this._getDataID,
817
823
  log: this._log,
818
824
  path: followupPayload.path,
@@ -897,6 +903,7 @@ class Executor<TMutation: MutationParameters> {
897
903
  ROOT_TYPE,
898
904
  {
899
905
  actorIdentifier: this._actorIdentifier,
906
+ deferDeduplicatedFields: false,
900
907
  getDataID: this._getDataID,
901
908
  log: this._log,
902
909
  path: [],
@@ -1363,6 +1370,7 @@ class Executor<TMutation: MutationParameters> {
1363
1370
  placeholder.typeName,
1364
1371
  {
1365
1372
  actorIdentifier: this._actorIdentifier,
1373
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
1366
1374
  getDataID: this._getDataID,
1367
1375
  log: this._log,
1368
1376
  path: placeholder.path,
@@ -1596,6 +1604,7 @@ class Executor<TMutation: MutationParameters> {
1596
1604
  typeName,
1597
1605
  {
1598
1606
  actorIdentifier: this._actorIdentifier,
1607
+ deferDeduplicatedFields: false,
1599
1608
  getDataID: this._getDataID,
1600
1609
  log: this._log,
1601
1610
  path: [...normalizationPath, responseKey, String(itemIndex)],
@@ -67,6 +67,7 @@ export type EnvironmentConfig = {
67
67
  +configName?: string,
68
68
  +handlerProvider?: ?HandlerProvider,
69
69
  +treatMissingFieldsAsNull?: boolean,
70
+ +deferDeduplicatedFields?: boolean,
70
71
  +log?: ?LogFunction,
71
72
  +operationLoader?: ?OperationLoader,
72
73
  +network: INetwork,
@@ -97,6 +98,7 @@ class RelayModernEnvironment implements IEnvironment {
97
98
  _operationTracker: OperationTracker;
98
99
  _getDataID: GetDataID;
99
100
  _treatMissingFieldsAsNull: boolean;
101
+ _deferDeduplicatedFields: boolean;
100
102
  _operationExecutions: Map<string, ActiveState>;
101
103
  +options: mixed;
102
104
  +_isServer: boolean;
@@ -106,6 +108,7 @@ class RelayModernEnvironment implements IEnvironment {
106
108
  constructor(config: EnvironmentConfig) {
107
109
  this.configName = config.configName;
108
110
  this._treatMissingFieldsAsNull = config.treatMissingFieldsAsNull === true;
111
+ this._deferDeduplicatedFields = config.deferDeduplicatedFields === true;
109
112
  const operationLoader = config.operationLoader;
110
113
  if (__DEV__) {
111
114
  if (operationLoader != null) {
@@ -501,6 +504,7 @@ class RelayModernEnvironment implements IEnvironment {
501
504
  // when the Observable is executed.
502
505
  source: createSource(),
503
506
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
507
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
504
508
  updater,
505
509
  });
506
510
  return () => executor.cancel();
@@ -116,6 +116,7 @@ class RelayModernStore implements Store {
116
116
  _resolverContext: ?ResolverContext;
117
117
  _actorIdentifier: ?ActorIdentifier;
118
118
  _treatMissingFieldsAsNull: boolean;
119
+ _deferDeduplicatedFields: boolean;
119
120
 
120
121
  constructor(
121
122
  source: MutableRecordSource,
@@ -135,6 +136,7 @@ class RelayModernStore implements Store {
135
136
  // These additional config options are only used if the experimental
136
137
  // @outputType resolver feature is used
137
138
  treatMissingFieldsAsNull?: ?boolean,
139
+ deferDeduplicatedFields?: ?boolean,
138
140
  actorIdentifier?: ?ActorIdentifier,
139
141
  },
140
142
  ) {
@@ -183,6 +185,7 @@ class RelayModernStore implements Store {
183
185
  options?.shouldProcessClientComponents ?? false;
184
186
 
185
187
  this._treatMissingFieldsAsNull = options?.treatMissingFieldsAsNull ?? false;
188
+ this._deferDeduplicatedFields = options?.deferDeduplicatedFields ?? false;
186
189
  this._actorIdentifier = options?.actorIdentifier;
187
190
 
188
191
  initializeRecordSource(this._recordSource);
@@ -402,6 +405,7 @@ class RelayModernStore implements Store {
402
405
  const snapshot = RelayReader.read(
403
406
  source,
404
407
  selector,
408
+ log,
405
409
  this._resolverCache,
406
410
  this._resolverContext,
407
411
  );
@@ -883,6 +887,7 @@ class RelayModernStore implements Store {
883
887
  getDataID: this._getDataID,
884
888
  log: this.__log,
885
889
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
890
+ deferDeduplicatedFields: this._deferDeduplicatedFields,
886
891
  shouldProcessClientComponents: this._shouldProcessClientComponents,
887
892
  actorIdentifier: this._actorIdentifier,
888
893
  };
@@ -464,7 +464,16 @@ function lookupSelector(
464
464
  source: RecordSource,
465
465
  selector: SingularReaderSelector,
466
466
  ): ?SelectorData {
467
- const selectorData = RelayReader.read(source, selector).data;
467
+ const selectorData = RelayReader.read(
468
+ source,
469
+ selector,
470
+ // The reader here is used to read the selector data for the updater.
471
+ // It is normal to not use all data in the update, so we don't pass
472
+ // the logger to skip tracking usages.
473
+ null,
474
+ undefined,
475
+ undefined,
476
+ ).data;
468
477
  if (__DEV__) {
469
478
  const deepFreeze = require('../util/deepFreeze');
470
479
  if (selectorData) {
@@ -37,6 +37,7 @@ import type {
37
37
  DataIDSet,
38
38
  FieldError,
39
39
  FieldErrors,
40
+ LogFunction,
40
41
  MissingClientEdgeRequestInfo,
41
42
  Record,
42
43
  RecordSource,
@@ -78,12 +79,14 @@ const invariant = require('invariant');
78
79
  function read(
79
80
  recordSource: RecordSource,
80
81
  selector: SingularReaderSelector,
82
+ log: ?LogFunction,
81
83
  resolverCache?: ResolverCache,
82
84
  resolverContext?: ResolverContext,
83
85
  ): Snapshot {
84
86
  const reader = new RelayReader(
85
87
  recordSource,
86
88
  selector,
89
+ log,
87
90
  resolverCache ?? new NoopResolverCache(),
88
91
  resolverContext,
89
92
  );
@@ -115,10 +118,15 @@ class RelayReader {
115
118
  _resolverCache: ResolverCache;
116
119
  _fragmentName: string;
117
120
  _resolverContext: ?ResolverContext;
121
+ // The log function currently is only used to log fragment spread accesses for unused
122
+ // fragments detection, so it is not passed in from all callsites. If we decide to
123
+ // extend the logging, this needs to be updated.
124
+ _log: ?LogFunction;
118
125
 
119
126
  constructor(
120
127
  recordSource: RecordSource,
121
128
  selector: SingularReaderSelector,
129
+ log: ?LogFunction,
122
130
  resolverCache: ResolverCache,
123
131
  resolverContext: ?ResolverContext,
124
132
  ) {
@@ -144,6 +152,7 @@ class RelayReader {
144
152
  this._fragmentName = selector.node.name;
145
153
  this._updatedDataIDs = new Set();
146
154
  this._resolverContext = resolverContext;
155
+ this._log = log;
147
156
  }
148
157
 
149
158
  read(): Snapshot {
@@ -200,6 +209,14 @@ class RelayReader {
200
209
  this._resolverCache.notifyUpdatedSubscribers(this._updatedDataIDs);
201
210
  this._updatedDataIDs.clear();
202
211
  }
212
+
213
+ if (RelayFeatureFlags.ENABLE_READER_FRAGMENTS_LOGGING) {
214
+ this._log?.({
215
+ name: 'reader.read',
216
+ selector: this._selector,
217
+ });
218
+ }
219
+
203
220
  return {
204
221
  data,
205
222
  fieldErrors: this._fieldErrors,
@@ -770,7 +787,12 @@ class RelayReader {
770
787
  snapshot = read(
771
788
  this._recordSource,
772
789
  singularReaderSelector,
790
+ // This reads data for a rootFragment in read time resolvers. There is no fragment
791
+ // spread created for it so the existing fragment spread logger can't be used to log
792
+ // unused rootFragments. Thus we skip passing the logger here for now.
793
+ null,
773
794
  this._resolverCache,
795
+ undefined,
774
796
  );
775
797
 
776
798
  return {
@@ -1583,12 +1605,13 @@ class RelayReader {
1583
1605
  if (data[ID_KEY] == null) {
1584
1606
  data[ID_KEY] = RelayModernRecord.getDataID(record);
1585
1607
  }
1586
- // $FlowFixMe[cannot-write] - writing into read-only field
1587
- fragmentPointers[fragmentSpread.name] = getArgumentValues(
1608
+ const args = getArgumentValues(
1588
1609
  fragmentSpread.args,
1589
1610
  this._variables,
1590
1611
  this._isWithinUnmatchedTypeRefinement,
1591
1612
  );
1613
+ // $FlowFixMe[cannot-write] - writing into read-only field
1614
+ fragmentPointers[fragmentSpread.name] = args;
1592
1615
  data[FRAGMENT_OWNER_KEY] = this._owner;
1593
1616
 
1594
1617
  if (
@@ -1599,6 +1622,14 @@ class RelayReader {
1599
1622
  ) {
1600
1623
  data[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
1601
1624
  }
1625
+
1626
+ if (RelayFeatureFlags.ENABLE_READER_FRAGMENTS_LOGGING) {
1627
+ this._log?.({
1628
+ name: 'reader.fragmentSpread',
1629
+ fragmentName: fragmentSpread.name,
1630
+ data,
1631
+ });
1632
+ }
1602
1633
  }
1603
1634
 
1604
1635
  _createInlineDataOrResolverFragmentPointer(
@@ -76,6 +76,7 @@ export type GetDataID = (
76
76
  export type NormalizationOptions = {
77
77
  +getDataID: GetDataID,
78
78
  +treatMissingFieldsAsNull: boolean,
79
+ +deferDeduplicatedFields: boolean,
79
80
  +log: ?LogFunction,
80
81
  +path?: $ReadOnlyArray<string>,
81
82
  +shouldProcessClientComponents?: ?boolean,
@@ -114,6 +115,7 @@ class RelayResponseNormalizer {
114
115
  _getDataId: GetDataID;
115
116
  _handleFieldPayloads: Array<HandleFieldPayload>;
116
117
  _treatMissingFieldsAsNull: boolean;
118
+ _deferDeduplicatedFields: boolean;
117
119
  _incrementalPlaceholders: Array<IncrementalDataPlaceholder>;
118
120
  _isClientExtension: boolean;
119
121
  _isUnmatchedAbstractType: boolean;
@@ -136,6 +138,7 @@ class RelayResponseNormalizer {
136
138
  this._getDataId = options.getDataID;
137
139
  this._handleFieldPayloads = [];
138
140
  this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
141
+ this._deferDeduplicatedFields = options.deferDeduplicatedFields;
139
142
  this._incrementalPlaceholders = [];
140
143
  this._isClientExtension = false;
141
144
  this._isUnmatchedAbstractType = false;
@@ -525,7 +528,9 @@ class RelayResponseNormalizer {
525
528
  // client is assumed to be correctly configured with
526
529
  // treatMissingFieldsAsNull=true.
527
530
  const isOptionalField =
528
- this._isClientExtension || this._isUnmatchedAbstractType;
531
+ this._isClientExtension ||
532
+ this._isUnmatchedAbstractType ||
533
+ this._deferDeduplicatedFields;
529
534
 
530
535
  if (isOptionalField) {
531
536
  // Field not expected to exist regardless of whether the server is pruning null
@@ -101,6 +101,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
101
101
  const backup = RelayReader.read(
102
102
  source,
103
103
  snapshot.selector,
104
+ this.__log,
104
105
  this._resolverCache,
105
106
  this._resolverContext,
106
107
  );
@@ -212,6 +213,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
212
213
  ? RelayReader.read(
213
214
  source,
214
215
  snapshot.selector,
216
+ this.__log,
215
217
  this._resolverCache,
216
218
  this._resolverContext,
217
219
  )
@@ -777,6 +777,17 @@ export type StoreNotifySubscriptionLogEvent = {
777
777
  +nextSnapshot: Snapshot,
778
778
  };
779
779
 
780
+ export type ReaderReadFragmentSpread = {
781
+ +name: 'reader.fragmentSpread',
782
+ fragmentName: string,
783
+ data: SelectorData,
784
+ };
785
+
786
+ export type ReaderRead = {
787
+ name: 'reader.read',
788
+ selector: SingularReaderSelector,
789
+ };
790
+
780
791
  export type EntrypointRootConsumeLogEvent = {
781
792
  +name: 'entrypoint.root.consume',
782
793
  +profilerContext: mixed,
@@ -855,7 +866,9 @@ export type LogEvent =
855
866
  | LiveResolverBatchStartLogEvent
856
867
  | LiveResolverBatchEndLogEvent
857
868
  | UseFragmentSubscriptionMissedUpdates
858
- | FetchQueryFetchLogEvent;
869
+ | FetchQueryFetchLogEvent
870
+ | ReaderRead
871
+ | ReaderReadFragmentSpread;
859
872
 
860
873
  export type LogFunction = LogEvent => void;
861
874
  export type LogRequestInfoFunction = mixed => void;
@@ -25,12 +25,15 @@ function createError(
25
25
  String(messageParams[index++]),
26
26
  );
27
27
  const err = new Error(message);
28
+ // messageParams[2] is the operation name
29
+ const operation = messageParams[2] ?? null;
28
30
  // $FlowFixMe[unsafe-object-assign]
29
31
  const error = Object.assign(err as any, {
30
32
  name,
31
33
  messageFormat,
32
34
  messageParams,
33
35
  type,
36
+ operation,
34
37
  taalOpcodes: [2, 2], // skip frame (code=2) twice
35
38
  });
36
39
  // In V8, Error objects keep the closure scope chain alive until the
@@ -87,6 +87,9 @@ export type FeatureFlags = {
87
87
 
88
88
  // Reduce the work on store.notify
89
89
  OPTIMIZE_NOTIFY: boolean,
90
+
91
+ // Enable logging for reader reading fragment spreads and fragments. Useful for logging unused fragments.
92
+ ENABLE_READER_FRAGMENTS_LOGGING: boolean,
90
93
  };
91
94
 
92
95
  const RelayFeatureFlags: FeatureFlags = {
@@ -118,6 +121,7 @@ const RelayFeatureFlags: FeatureFlags = {
118
121
  CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false,
119
122
  FILTER_OUT_RELAY_RESOLVER_RECORDS: false,
120
123
  OPTIMIZE_NOTIFY: false,
124
+ ENABLE_READER_FRAGMENTS_LOGGING: false,
121
125
  };
122
126
 
123
127
  module.exports = RelayFeatureFlags;