react-relay 11.0.2 → 13.0.0-rc.2

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.
Files changed (177) hide show
  1. package/README.md +47 -0
  2. package/ReactRelayContainerUtils.js.flow +1 -1
  3. package/ReactRelayContext.js +1 -1
  4. package/ReactRelayContext.js.flow +3 -4
  5. package/ReactRelayFragmentContainer.js.flow +25 -25
  6. package/ReactRelayFragmentMockRenderer.js.flow +2 -2
  7. package/ReactRelayLocalQueryRenderer.js.flow +7 -8
  8. package/ReactRelayPaginationContainer.js.flow +112 -59
  9. package/ReactRelayQueryFetcher.js.flow +10 -11
  10. package/ReactRelayQueryRenderer.js.flow +116 -82
  11. package/ReactRelayQueryRendererContext.js.flow +1 -1
  12. package/ReactRelayRefetchContainer.js.flow +42 -39
  13. package/ReactRelayTestMocker.js.flow +17 -15
  14. package/ReactRelayTypes.js.flow +11 -11
  15. package/RelayContext.js.flow +4 -4
  16. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +2 -3
  17. package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +12 -8
  18. package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +11 -7
  19. package/__flowtests__/RelayModern-flowtest.js.flow +79 -47
  20. package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +6 -5
  21. package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +6 -5
  22. package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +5 -4
  23. package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +5 -4
  24. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +72 -0
  25. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +72 -0
  26. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +227 -0
  27. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +164 -0
  28. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +227 -0
  29. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +164 -0
  30. package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +66 -0
  31. package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +66 -0
  32. package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +59 -0
  33. package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +61 -0
  34. package/assertFragmentMap.js.flow +3 -3
  35. package/buildReactRelayContainer.js.flow +12 -11
  36. package/getRootVariablesForFragments.js.flow +3 -5
  37. package/hooks.js +1 -1
  38. package/hooks.js.flow +6 -7
  39. package/index.js +1 -1
  40. package/index.js.flow +7 -8
  41. package/isRelayEnvironment.js.flow +1 -1
  42. package/jest-react/enqueueTask.js.flow +56 -0
  43. package/jest-react/index.js.flow +12 -0
  44. package/jest-react/internalAct.js.flow +138 -0
  45. package/legacy.js +1 -1
  46. package/legacy.js.flow +1 -1
  47. package/lib/ReactRelayContainerUtils.js +1 -1
  48. package/lib/ReactRelayContext.js +1 -1
  49. package/lib/ReactRelayFragmentContainer.js +22 -16
  50. package/lib/ReactRelayFragmentMockRenderer.js +3 -3
  51. package/lib/ReactRelayLocalQueryRenderer.js +8 -9
  52. package/lib/ReactRelayPaginationContainer.js +97 -39
  53. package/lib/ReactRelayQueryFetcher.js +3 -3
  54. package/lib/ReactRelayQueryRenderer.js +87 -54
  55. package/lib/ReactRelayQueryRendererContext.js +1 -1
  56. package/lib/ReactRelayRefetchContainer.js +39 -26
  57. package/lib/ReactRelayTestMocker.js +8 -9
  58. package/lib/ReactRelayTypes.js +1 -1
  59. package/lib/RelayContext.js +4 -3
  60. package/lib/assertFragmentMap.js +3 -2
  61. package/lib/buildReactRelayContainer.js +8 -8
  62. package/lib/getRootVariablesForFragments.js +2 -3
  63. package/lib/hooks.js +6 -6
  64. package/lib/index.js +8 -8
  65. package/lib/isRelayEnvironment.js +1 -1
  66. package/lib/jest-react/enqueueTask.js +53 -0
  67. package/lib/jest-react/index.js +13 -0
  68. package/lib/jest-react/internalAct.js +115 -0
  69. package/lib/legacy.js +1 -1
  70. package/lib/multi-actor/ActorChange.js +30 -0
  71. package/lib/multi-actor/index.js +11 -0
  72. package/lib/multi-actor/useRelayActorEnvironment.js +29 -0
  73. package/lib/readContext.js +1 -1
  74. package/lib/relay-hooks/EntryPointContainer.react.js +4 -4
  75. package/lib/relay-hooks/EntryPointTypes.flow.js +1 -1
  76. package/lib/relay-hooks/FragmentResource.js +342 -89
  77. package/lib/relay-hooks/InternalLogger.js +1 -1
  78. package/lib/relay-hooks/LRUCache.js +1 -1
  79. package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +5 -5
  80. package/lib/relay-hooks/MatchContainer.js +2 -2
  81. package/lib/relay-hooks/ProfilerContext.js +1 -1
  82. package/lib/relay-hooks/QueryResource.js +172 -29
  83. package/lib/relay-hooks/RelayEnvironmentProvider.js +6 -4
  84. package/lib/relay-hooks/SuspenseResource.js +130 -0
  85. package/lib/relay-hooks/loadEntryPoint.js +1 -1
  86. package/lib/relay-hooks/loadQuery.js +42 -20
  87. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +25 -16
  88. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +1 -1
  89. package/lib/relay-hooks/useBlockingPaginationFragment.js +5 -6
  90. package/lib/relay-hooks/useEntryPointLoader.js +3 -3
  91. package/lib/relay-hooks/useFetchTrackingRef.js +3 -2
  92. package/lib/relay-hooks/useFragment.js +7 -7
  93. package/lib/relay-hooks/useFragmentNode.js +5 -5
  94. package/lib/relay-hooks/useIsMountedRef.js +1 -1
  95. package/lib/relay-hooks/useIsOperationNodeActive.js +3 -3
  96. package/lib/relay-hooks/useIsParentQueryActive.js +1 -1
  97. package/lib/relay-hooks/useLazyLoadQuery.js +4 -4
  98. package/lib/relay-hooks/useLazyLoadQueryNode.js +11 -5
  99. package/lib/relay-hooks/useLoadMoreFunction.js +9 -13
  100. package/lib/relay-hooks/useMemoOperationDescriptor.js +3 -3
  101. package/lib/relay-hooks/useMemoVariables.js +3 -3
  102. package/lib/relay-hooks/useMutation.js +18 -7
  103. package/lib/relay-hooks/usePaginationFragment.js +3 -4
  104. package/lib/relay-hooks/usePreloadedQuery.js +6 -6
  105. package/lib/relay-hooks/useQueryLoader.js +31 -11
  106. package/lib/relay-hooks/useRefetchableFragment.js +1 -1
  107. package/lib/relay-hooks/useRefetchableFragmentNode.js +14 -18
  108. package/lib/relay-hooks/useRelayEnvironment.js +3 -3
  109. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +3 -3
  110. package/lib/relay-hooks/useSubscribeToInvalidationState.js +3 -2
  111. package/lib/relay-hooks/useSubscription.js +11 -8
  112. package/multi-actor/ActorChange.js.flow +58 -0
  113. package/multi-actor/index.js.flow +14 -0
  114. package/multi-actor/useRelayActorEnvironment.js.flow +49 -0
  115. package/package.json +3 -3
  116. package/react-relay-hooks.js +2 -2
  117. package/react-relay-hooks.min.js +2 -2
  118. package/react-relay-legacy.js +2 -2
  119. package/react-relay-legacy.min.js +2 -2
  120. package/react-relay.js +2 -2
  121. package/react-relay.min.js +2 -2
  122. package/readContext.js.flow +1 -1
  123. package/relay-hooks/EntryPointContainer.react.js.flow +9 -16
  124. package/relay-hooks/EntryPointTypes.flow.js.flow +25 -26
  125. package/relay-hooks/FragmentResource.js.flow +359 -93
  126. package/relay-hooks/InternalLogger.js.flow +1 -1
  127. package/relay-hooks/LRUCache.js.flow +1 -1
  128. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +33 -47
  129. package/relay-hooks/MatchContainer.js.flow +4 -3
  130. package/relay-hooks/ProfilerContext.js.flow +1 -1
  131. package/relay-hooks/QueryResource.js.flow +217 -26
  132. package/relay-hooks/RelayEnvironmentProvider.js.flow +15 -5
  133. package/relay-hooks/SuspenseResource.js.flow +115 -0
  134. package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +5 -4
  135. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +2 -2
  136. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +59 -0
  137. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +61 -0
  138. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +11 -10
  139. package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +55 -32
  140. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +11 -10
  141. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +11 -10
  142. package/relay-hooks/__flowtests__/utils.js.flow +21 -32
  143. package/relay-hooks/loadEntryPoint.js.flow +7 -13
  144. package/relay-hooks/loadQuery.js.flow +50 -32
  145. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +31 -22
  146. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +7 -13
  147. package/relay-hooks/useBlockingPaginationFragment.js.flow +14 -12
  148. package/relay-hooks/useEntryPointLoader.js.flow +8 -11
  149. package/relay-hooks/useFetchTrackingRef.js.flow +3 -3
  150. package/relay-hooks/useFragment.js.flow +31 -62
  151. package/relay-hooks/useFragmentNode.js.flow +6 -8
  152. package/relay-hooks/useIsMountedRef.js.flow +1 -1
  153. package/relay-hooks/useIsOperationNodeActive.js.flow +4 -6
  154. package/relay-hooks/useIsParentQueryActive.js.flow +4 -5
  155. package/relay-hooks/useLazyLoadQuery.js.flow +14 -16
  156. package/relay-hooks/useLazyLoadQueryNode.js.flow +20 -14
  157. package/relay-hooks/useLoadMoreFunction.js.flow +21 -30
  158. package/relay-hooks/useMemoOperationDescriptor.js.flow +6 -8
  159. package/relay-hooks/useMemoVariables.js.flow +7 -7
  160. package/relay-hooks/useMutation.js.flow +27 -27
  161. package/relay-hooks/usePaginationFragment.js.flow +39 -45
  162. package/relay-hooks/usePreloadedQuery.js.flow +14 -20
  163. package/relay-hooks/useQueryLoader.js.flow +42 -23
  164. package/relay-hooks/useRefetchableFragment.js.flow +8 -9
  165. package/relay-hooks/useRefetchableFragmentNode.js.flow +25 -33
  166. package/relay-hooks/useRelayEnvironment.js.flow +3 -5
  167. package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -4
  168. package/relay-hooks/useSubscribeToInvalidationState.js.flow +4 -7
  169. package/relay-hooks/useSubscription.js.flow +21 -11
  170. package/lib/relay-hooks/getPaginationMetadata.js +0 -41
  171. package/lib/relay-hooks/getPaginationVariables.js +0 -67
  172. package/lib/relay-hooks/getRefetchMetadata.js +0 -36
  173. package/lib/relay-hooks/getValueAtPath.js +0 -51
  174. package/relay-hooks/getPaginationMetadata.js.flow +0 -74
  175. package/relay-hooks/getPaginationVariables.js.flow +0 -110
  176. package/relay-hooks/getRefetchMetadata.js.flow +0 -80
  177. package/relay-hooks/getValueAtPath.js.flow +0 -46
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -15,19 +15,32 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
15
15
 
16
16
  var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
17
17
 
18
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
19
+
18
20
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
19
21
 
20
22
  var LRUCache = require('./LRUCache');
21
23
 
24
+ var _require = require('./QueryResource'),
25
+ getQueryResourceForEnvironment = _require.getQueryResourceForEnvironment;
26
+
27
+ var SuspenseResource = require('./SuspenseResource');
28
+
22
29
  var invariant = require('invariant');
23
30
 
24
- var _require = require('relay-runtime'),
25
- getPromiseForActiveRequest = _require.__internal.getPromiseForActiveRequest,
26
- getFragmentIdentifier = _require.getFragmentIdentifier,
27
- getSelector = _require.getSelector,
28
- isPromise = _require.isPromise,
29
- recycleNodesInto = _require.recycleNodesInto,
30
- reportMissingRequiredFields = _require.reportMissingRequiredFields;
31
+ var _require2 = require('relay-runtime'),
32
+ RelayFeatureFlags = _require2.RelayFeatureFlags,
33
+ _require2$__internal = _require2.__internal,
34
+ fetchQuery = _require2$__internal.fetchQuery,
35
+ getPromiseForActiveRequest = _require2$__internal.getPromiseForActiveRequest,
36
+ createOperationDescriptor = _require2.createOperationDescriptor,
37
+ getFragmentIdentifier = _require2.getFragmentIdentifier,
38
+ getPendingOperationsForFragment = _require2.getPendingOperationsForFragment,
39
+ getSelector = _require2.getSelector,
40
+ getVariablesFromFragment = _require2.getVariablesFromFragment,
41
+ isPromise = _require2.isPromise,
42
+ recycleNodesInto = _require2.recycleNodesInto,
43
+ reportMissingRequiredFields = _require2.reportMissingRequiredFields;
31
44
 
32
45
  var WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
33
46
  // TODO: Fix to not rely on LRU. If the number of active fragments exceeds this
@@ -47,32 +60,133 @@ function isMissingData(snapshot) {
47
60
  return snapshot.isMissingData;
48
61
  }
49
62
 
50
- function getFragmentResult(cacheKey, snapshot) {
63
+ function hasMissingClientEdges(snapshot) {
64
+ var _snapshot$missingClie, _snapshot$missingClie2;
65
+
66
+ if (Array.isArray(snapshot)) {
67
+ return snapshot.some(function (s) {
68
+ var _s$missingClientEdges, _s$missingClientEdges2;
69
+
70
+ return ((_s$missingClientEdges = (_s$missingClientEdges2 = s.missingClientEdges) === null || _s$missingClientEdges2 === void 0 ? void 0 : _s$missingClientEdges2.length) !== null && _s$missingClientEdges !== void 0 ? _s$missingClientEdges : 0) > 0;
71
+ });
72
+ }
73
+
74
+ return ((_snapshot$missingClie = (_snapshot$missingClie2 = snapshot.missingClientEdges) === null || _snapshot$missingClie2 === void 0 ? void 0 : _snapshot$missingClie2.length) !== null && _snapshot$missingClie !== void 0 ? _snapshot$missingClie : 0) > 0;
75
+ }
76
+
77
+ function singularOrPluralForEach(snapshot, f) {
78
+ if (Array.isArray(snapshot)) {
79
+ snapshot.forEach(f);
80
+ } else {
81
+ f(snapshot);
82
+ }
83
+ }
84
+
85
+ function getFragmentResult(cacheKey, snapshot, storeEpoch) {
51
86
  if (Array.isArray(snapshot)) {
52
87
  return {
53
88
  cacheKey: cacheKey,
54
89
  snapshot: snapshot,
55
90
  data: snapshot.map(function (s) {
56
91
  return s.data;
57
- })
92
+ }),
93
+ isMissingData: isMissingData(snapshot),
94
+ storeEpoch: storeEpoch
58
95
  };
59
96
  }
60
97
 
61
98
  return {
62
99
  cacheKey: cacheKey,
63
100
  snapshot: snapshot,
64
- data: snapshot.data
101
+ data: snapshot.data,
102
+ isMissingData: isMissingData(snapshot),
103
+ storeEpoch: storeEpoch
65
104
  };
66
105
  }
106
+ /**
107
+ * The purpose of this cache is to allow information to be passed from an
108
+ * initial read which suspends through to the commit that follows a subsequent
109
+ * successful read. Specifically, the QueryResource result for the data fetch
110
+ * is passed through so that that query can be retained on commit.
111
+ */
67
112
 
68
- function getPromiseForPendingOperationAffectingOwner(environment, request) {
69
- return environment.getOperationTracker().getPromiseForPendingOperationsAffectingOwner(request);
70
- }
113
+
114
+ var ClientEdgeQueryResultsCache = /*#__PURE__*/function () {
115
+ function ClientEdgeQueryResultsCache(environment) {
116
+ (0, _defineProperty2["default"])(this, "_cache", new Map());
117
+ (0, _defineProperty2["default"])(this, "_retainCounts", new Map());
118
+ this._environment = environment;
119
+ }
120
+
121
+ var _proto = ClientEdgeQueryResultsCache.prototype;
122
+
123
+ _proto.get = function get(fragmentIdentifier) {
124
+ var _this$_cache$get$, _this$_cache$get;
125
+
126
+ return (_this$_cache$get$ = (_this$_cache$get = this._cache.get(fragmentIdentifier)) === null || _this$_cache$get === void 0 ? void 0 : _this$_cache$get[0]) !== null && _this$_cache$get$ !== void 0 ? _this$_cache$get$ : undefined;
127
+ };
128
+
129
+ _proto.recordQueryResults = function recordQueryResults(fragmentIdentifier, value) {
130
+ var _this = this;
131
+
132
+ var existing = this._cache.get(fragmentIdentifier);
133
+
134
+ if (!existing) {
135
+ var suspenseResource = new SuspenseResource(function () {
136
+ return _this._retain(fragmentIdentifier);
137
+ });
138
+
139
+ this._cache.set(fragmentIdentifier, [value, suspenseResource]);
140
+
141
+ suspenseResource.temporaryRetain(this._environment);
142
+ } else {
143
+ var existingResults = existing[0],
144
+ _suspenseResource = existing[1];
145
+ value.forEach(function (queryResult) {
146
+ existingResults.push(queryResult);
147
+ });
148
+
149
+ _suspenseResource.temporaryRetain(this._environment);
150
+ }
151
+ };
152
+
153
+ _proto._retain = function _retain(id) {
154
+ var _this2 = this;
155
+
156
+ var _this$_retainCounts$g;
157
+
158
+ var retainCount = ((_this$_retainCounts$g = this._retainCounts.get(id)) !== null && _this$_retainCounts$g !== void 0 ? _this$_retainCounts$g : 0) + 1;
159
+
160
+ this._retainCounts.set(id, retainCount);
161
+
162
+ return {
163
+ dispose: function dispose() {
164
+ var _this$_retainCounts$g2;
165
+
166
+ var newRetainCount = ((_this$_retainCounts$g2 = _this2._retainCounts.get(id)) !== null && _this$_retainCounts$g2 !== void 0 ? _this$_retainCounts$g2 : 0) - 1;
167
+
168
+ if (newRetainCount > 0) {
169
+ _this2._retainCounts.set(id, newRetainCount);
170
+ } else {
171
+ _this2._retainCounts["delete"](id);
172
+
173
+ _this2._cache["delete"](id);
174
+ }
175
+ }
176
+ };
177
+ };
178
+
179
+ return ClientEdgeQueryResultsCache;
180
+ }();
71
181
 
72
182
  var FragmentResourceImpl = /*#__PURE__*/function () {
73
183
  function FragmentResourceImpl(environment) {
74
184
  this._environment = environment;
75
185
  this._cache = LRUCache.create(CACHE_CAPACITY);
186
+
187
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
188
+ this._clientEdgeQueryResultsCache = new ClientEdgeQueryResultsCache(environment);
189
+ }
76
190
  }
77
191
  /**
78
192
  * This function should be called during a Component's render function,
@@ -81,9 +195,9 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
81
195
  */
82
196
 
83
197
 
84
- var _proto = FragmentResourceImpl.prototype;
198
+ var _proto2 = FragmentResourceImpl.prototype;
85
199
 
86
- _proto.read = function read(fragmentNode, fragmentRef, componentDisplayName, fragmentKey) {
200
+ _proto2.read = function read(fragmentNode, fragmentRef, componentDisplayName, fragmentKey) {
87
201
  return this.readWithIdentifier(fragmentNode, fragmentRef, getFragmentIdentifier(fragmentNode, fragmentRef), componentDisplayName, fragmentKey);
88
202
  }
89
203
  /**
@@ -93,8 +207,10 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
93
207
  */
94
208
  ;
95
209
 
96
- _proto.readWithIdentifier = function readWithIdentifier(fragmentNode, fragmentRef, fragmentIdentifier, componentDisplayName, fragmentKey) {
97
- var _fragmentNode$metadat;
210
+ _proto2.readWithIdentifier = function readWithIdentifier(fragmentNode, fragmentRef, fragmentIdentifier, componentDisplayName, fragmentKey) {
211
+ var _this3 = this;
212
+
213
+ var _fragmentNode$metadat, _clientEdgePromises;
98
214
 
99
215
  var environment = this._environment; // If fragmentRef is null or undefined, pass it directly through.
100
216
  // This is a convenience when consuming fragments via a HOC API, when the
@@ -104,11 +220,14 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
104
220
  return {
105
221
  cacheKey: fragmentIdentifier,
106
222
  data: null,
107
- snapshot: null
223
+ isMissingData: false,
224
+ snapshot: null,
225
+ storeEpoch: 0
108
226
  };
109
- } // If fragmentRef is plural, ensure that it is an array.
110
- // If it's empty, return the empty array directly before doing any more work.
227
+ }
111
228
 
229
+ var storeEpoch = environment.getStore().getEpoch(); // If fragmentRef is plural, ensure that it is an array.
230
+ // If it's empty, return the empty array directly before doing any more work.
112
231
 
113
232
  if ((fragmentNode === null || fragmentNode === void 0 ? void 0 : (_fragmentNode$metadat = fragmentNode.metadata) === null || _fragmentNode$metadat === void 0 ? void 0 : _fragmentNode$metadat.plural) === true) {
114
233
  !Array.isArray(fragmentRef) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` to be ' + 'an array, instead got `%s`. Remove `@relay(plural: true)` ' + 'from fragment `%s` to allow the prop to be an object.', fragmentKey != null ? " for key `".concat(fragmentKey, "`") : '', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
@@ -117,7 +236,9 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
117
236
  return {
118
237
  cacheKey: fragmentIdentifier,
119
238
  data: CONSTANT_READONLY_EMPTY_ARRAY,
120
- snapshot: CONSTANT_READONLY_EMPTY_ARRAY
239
+ isMissingData: false,
240
+ snapshot: CONSTANT_READONLY_EMPTY_ARRAY,
241
+ storeEpoch: storeEpoch
121
242
  };
122
243
  }
123
244
  } // Now we actually attempt to read the fragment:
@@ -127,14 +248,24 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
127
248
  var cachedValue = this._cache.get(fragmentIdentifier);
128
249
 
129
250
  if (cachedValue != null) {
130
- if (isPromise(cachedValue)) {
131
- throw cachedValue;
251
+ if (cachedValue.kind === 'pending' && isPromise(cachedValue.promise)) {
252
+ environment.__log({
253
+ name: 'suspense.fragment',
254
+ data: cachedValue.result.data,
255
+ fragment: fragmentNode,
256
+ isRelayHooks: true,
257
+ isMissingData: cachedValue.result.isMissingData,
258
+ isPromiseCached: true,
259
+ pendingOperations: cachedValue.pendingOperations
260
+ });
261
+
262
+ throw cachedValue.promise;
132
263
  }
133
264
 
134
- if (cachedValue.snapshot) {
135
- this._reportMissingRequiredFieldsInSnapshot(cachedValue.snapshot);
265
+ if (cachedValue.kind === 'done' && cachedValue.result.snapshot) {
266
+ this._reportMissingRequiredFieldsInSnapshot(cachedValue.result.snapshot);
136
267
 
137
- return cachedValue;
268
+ return cachedValue.result;
138
269
  }
139
270
  } // 2. If not, try reading the fragment from the Relay store.
140
271
  // If the snapshot has data, return it and save it in cache
@@ -145,41 +276,120 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
145
276
  var snapshot = fragmentSelector.kind === 'PluralReaderSelector' ? fragmentSelector.selectors.map(function (s) {
146
277
  return environment.lookup(s);
147
278
  }) : environment.lookup(fragmentSelector);
148
- var fragmentOwner = fragmentSelector.kind === 'PluralReaderSelector' ? fragmentSelector.selectors[0].owner : fragmentSelector.owner;
279
+ var fragmentResult = getFragmentResult(fragmentIdentifier, snapshot, storeEpoch);
149
280
 
150
- if (!isMissingData(snapshot)) {
281
+ if (!fragmentResult.isMissingData) {
151
282
  this._reportMissingRequiredFieldsInSnapshot(snapshot);
152
283
 
153
- var fragmentResult = getFragmentResult(fragmentIdentifier, snapshot);
154
-
155
- this._cache.set(fragmentIdentifier, fragmentResult);
284
+ this._cache.set(fragmentIdentifier, {
285
+ kind: 'done',
286
+ result: fragmentResult
287
+ });
156
288
 
157
289
  return fragmentResult;
158
- } // 3. If we don't have data in the store, check if a request is in
159
- // flight for the fragment's parent query, or for another operation
160
- // that may affect the parent's query data, such as a mutation
161
- // or subscription. If a promise exists, cache the promise and use it
162
- // to suspend.
290
+ } // 3. If we don't have data in the store, there's two cases where we should
291
+ // suspend to await the data: First if any client edges were traversed where
292
+ // the destination record was missing data; in that case we initiate a query
293
+ // here to fetch the missing data. Second, there may already be a request
294
+ // in flight for the fragment's parent query, or for another operation that
295
+ // may affect the parent's query data, such as a mutation or subscription.
296
+ // For any of these cases we can get a promise, which we will cache and
297
+ // suspend on.
298
+ // First, initiate a query for any client edges that were missing data:
299
+
300
+
301
+ var clientEdgeRequests = null;
302
+
303
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES && hasMissingClientEdges(snapshot)) {
304
+ clientEdgeRequests = [];
305
+ var queryResource = getQueryResourceForEnvironment(this._environment);
306
+ var queryResults = [];
307
+ singularOrPluralForEach(snapshot, function (snap) {
308
+ var _snap$missingClientEd;
309
+
310
+ (_snap$missingClientEd = snap.missingClientEdges) === null || _snap$missingClientEd === void 0 ? void 0 : _snap$missingClientEd.forEach(function (_ref) {
311
+ var _clientEdgeRequests;
312
+
313
+ var request = _ref.request,
314
+ clientEdgeDestinationID = _ref.clientEdgeDestinationID;
315
+
316
+ var _this3$_performClient = _this3._performClientEdgeQuery(queryResource, fragmentNode, fragmentRef, request, clientEdgeDestinationID),
317
+ queryResult = _this3$_performClient.queryResult,
318
+ requestDescriptor = _this3$_performClient.requestDescriptor;
319
+
320
+ queryResults.push(queryResult);
321
+ (_clientEdgeRequests = clientEdgeRequests) === null || _clientEdgeRequests === void 0 ? void 0 : _clientEdgeRequests.push(requestDescriptor);
322
+ });
323
+ }); // Store the query so that it can be retained when our own fragment is
324
+ // subscribed to. This merges with any existing query results:
325
+
326
+ !(this._clientEdgeQueryResultsCache != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Client edge query result cache should exist when ENABLE_CLIENT_EDGES is on.') : invariant(false) : void 0;
327
+
328
+ this._clientEdgeQueryResultsCache.recordQueryResults(fragmentIdentifier, queryResults);
329
+ }
330
+
331
+ var clientEdgePromises = null;
332
+
333
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES && clientEdgeRequests) {
334
+ clientEdgePromises = clientEdgeRequests.map(function (request) {
335
+ return getPromiseForActiveRequest(_this3._environment, request);
336
+ }).filter(function (p) {
337
+ return p != null;
338
+ });
339
+ } // Finally look for operations in flight for our parent query:
340
+
163
341
 
342
+ var fragmentOwner = fragmentSelector.kind === 'PluralReaderSelector' ? fragmentSelector.selectors[0].owner : fragmentSelector.owner;
343
+
344
+ var parentQueryPromiseResult = this._getAndSavePromiseForFragmentRequestInFlight(fragmentIdentifier, fragmentNode, fragmentOwner, fragmentResult);
345
+
346
+ var parentQueryPromiseResultPromise = parentQueryPromiseResult === null || parentQueryPromiseResult === void 0 ? void 0 : parentQueryPromiseResult.promise; // for refinement
347
+
348
+ if (((_clientEdgePromises = clientEdgePromises) === null || _clientEdgePromises === void 0 ? void 0 : _clientEdgePromises.length) || isPromise(parentQueryPromiseResultPromise)) {
349
+ var _parentQueryPromiseRe, _clientEdgeRequests2, _clientEdgePromises2;
164
350
 
165
- var networkPromise = this._getAndSavePromiseForFragmentRequestInFlight(fragmentIdentifier, fragmentNode, fragmentOwner);
351
+ environment.__log({
352
+ name: 'suspense.fragment',
353
+ data: fragmentResult.data,
354
+ fragment: fragmentNode,
355
+ isRelayHooks: true,
356
+ isPromiseCached: false,
357
+ isMissingData: fragmentResult.isMissingData,
358
+ pendingOperations: [].concat((0, _toConsumableArray2["default"])((_parentQueryPromiseRe = parentQueryPromiseResult === null || parentQueryPromiseResult === void 0 ? void 0 : parentQueryPromiseResult.pendingOperations) !== null && _parentQueryPromiseRe !== void 0 ? _parentQueryPromiseRe : []), (0, _toConsumableArray2["default"])((_clientEdgeRequests2 = clientEdgeRequests) !== null && _clientEdgeRequests2 !== void 0 ? _clientEdgeRequests2 : []))
359
+ });
166
360
 
167
- if (networkPromise != null) {
168
- throw networkPromise;
361
+ throw ((_clientEdgePromises2 = clientEdgePromises) === null || _clientEdgePromises2 === void 0 ? void 0 : _clientEdgePromises2.length) ? Promise.all([parentQueryPromiseResultPromise].concat((0, _toConsumableArray2["default"])(clientEdgePromises))) : parentQueryPromiseResultPromise;
169
362
  }
170
363
 
171
364
  this._reportMissingRequiredFieldsInSnapshot(snapshot);
172
365
 
173
- return getFragmentResult(fragmentIdentifier, snapshot);
366
+ return getFragmentResult(fragmentIdentifier, snapshot, storeEpoch);
174
367
  };
175
368
 
176
- _proto._reportMissingRequiredFieldsInSnapshot = function _reportMissingRequiredFieldsInSnapshot(snapshot) {
177
- var _this = this;
369
+ _proto2._performClientEdgeQuery = function _performClientEdgeQuery(queryResource, fragmentNode, fragmentRef, request, clientEdgeDestinationID) {
370
+ var originalVariables = getVariablesFromFragment(fragmentNode, fragmentRef);
371
+ var variables = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, originalVariables), {}, {
372
+ id: clientEdgeDestinationID // TODO should be a reserved name
373
+
374
+ });
375
+ var operation = createOperationDescriptor(request, variables, {} // TODO cacheConfig should probably inherent from parent operation
376
+ );
377
+ var fetchObservable = fetchQuery(this._environment, operation);
378
+ var queryResult = queryResource.prepare(operation, fetchObservable // TODO should inherent render policy etc. from parent operation
379
+ );
380
+ return {
381
+ requestDescriptor: operation.request,
382
+ queryResult: queryResult
383
+ };
384
+ };
385
+
386
+ _proto2._reportMissingRequiredFieldsInSnapshot = function _reportMissingRequiredFieldsInSnapshot(snapshot) {
387
+ var _this4 = this;
178
388
 
179
389
  if (Array.isArray(snapshot)) {
180
390
  snapshot.forEach(function (s) {
181
391
  if (s.missingRequiredFields != null) {
182
- reportMissingRequiredFields(_this._environment, s.missingRequiredFields);
392
+ reportMissingRequiredFields(_this4._environment, s.missingRequiredFields);
183
393
  }
184
394
  });
185
395
  } else {
@@ -189,7 +399,7 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
189
399
  }
190
400
  };
191
401
 
192
- _proto.readSpec = function readSpec(fragmentNodes, fragmentRefs, componentDisplayName) {
402
+ _proto2.readSpec = function readSpec(fragmentNodes, fragmentRefs, componentDisplayName) {
193
403
  var result = {};
194
404
 
195
405
  for (var _key in fragmentNodes) {
@@ -199,8 +409,8 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
199
409
  return result;
200
410
  };
201
411
 
202
- _proto.subscribe = function subscribe(fragmentResult, callback) {
203
- var _this2 = this;
412
+ _proto2.subscribe = function subscribe(fragmentResult, callback) {
413
+ var _this5 = this;
204
414
 
205
415
  var environment = this._environment;
206
416
  var cacheKey = fragmentResult.cacheKey;
@@ -225,42 +435,62 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
225
435
  } // 3. Establish subscriptions on the snapshot(s)
226
436
 
227
437
 
228
- var dataSubscriptions = [];
438
+ var disposables = [];
229
439
 
230
440
  if (Array.isArray(renderedSnapshot)) {
231
441
  !Array.isArray(currentSnapshot) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected snapshots to be plural. ' + "If you're seeing this, this is likely a bug in Relay.") : invariant(false) : void 0;
232
442
  currentSnapshot.forEach(function (snapshot, idx) {
233
- dataSubscriptions.push(environment.subscribe(snapshot, function (latestSnapshot) {
234
- _this2._updatePluralSnapshot(cacheKey, currentSnapshot, latestSnapshot, idx);
443
+ disposables.push(environment.subscribe(snapshot, function (latestSnapshot) {
444
+ var storeEpoch = environment.getStore().getEpoch();
445
+
446
+ _this5._updatePluralSnapshot(cacheKey, currentSnapshot, latestSnapshot, idx, storeEpoch);
235
447
 
236
448
  callback();
237
449
  }));
238
450
  });
239
451
  } else {
240
452
  !(currentSnapshot != null && !Array.isArray(currentSnapshot)) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected snapshot to be singular. ' + "If you're seeing this, this is likely a bug in Relay.") : invariant(false) : void 0;
241
- dataSubscriptions.push(environment.subscribe(currentSnapshot, function (latestSnapshot) {
242
- _this2._cache.set(cacheKey, getFragmentResult(cacheKey, latestSnapshot));
453
+ disposables.push(environment.subscribe(currentSnapshot, function (latestSnapshot) {
454
+ var storeEpoch = environment.getStore().getEpoch();
455
+
456
+ _this5._cache.set(cacheKey, {
457
+ kind: 'done',
458
+ result: getFragmentResult(cacheKey, latestSnapshot, storeEpoch)
459
+ });
243
460
 
244
461
  callback();
245
462
  }));
246
463
  }
247
464
 
465
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
466
+ var _this$_clientEdgeQuer, _this$_clientEdgeQuer2;
467
+
468
+ var clientEdgeQueryResults = (_this$_clientEdgeQuer = (_this$_clientEdgeQuer2 = this._clientEdgeQueryResultsCache) === null || _this$_clientEdgeQuer2 === void 0 ? void 0 : _this$_clientEdgeQuer2.get(cacheKey)) !== null && _this$_clientEdgeQuer !== void 0 ? _this$_clientEdgeQuer : undefined;
469
+
470
+ if (clientEdgeQueryResults === null || clientEdgeQueryResults === void 0 ? void 0 : clientEdgeQueryResults.length) {
471
+ var queryResource = getQueryResourceForEnvironment(this._environment);
472
+ clientEdgeQueryResults.forEach(function (queryResult) {
473
+ disposables.push(queryResource.retain(queryResult));
474
+ });
475
+ }
476
+ }
477
+
248
478
  return {
249
479
  dispose: function dispose() {
250
- dataSubscriptions.map(function (s) {
480
+ disposables.forEach(function (s) {
251
481
  return s.dispose();
252
482
  });
253
483
 
254
- _this2._cache["delete"](cacheKey);
484
+ _this5._cache["delete"](cacheKey);
255
485
  }
256
486
  };
257
487
  };
258
488
 
259
- _proto.subscribeSpec = function subscribeSpec(fragmentResults, callback) {
260
- var _this3 = this;
489
+ _proto2.subscribeSpec = function subscribeSpec(fragmentResults, callback) {
490
+ var _this6 = this;
261
491
 
262
492
  var disposables = Object.keys(fragmentResults).map(function (key) {
263
- return _this3.subscribe(fragmentResults[key], callback);
493
+ return _this6.subscribe(fragmentResults[key], callback);
264
494
  });
265
495
  return {
266
496
  dispose: function dispose() {
@@ -271,18 +501,26 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
271
501
  };
272
502
  };
273
503
 
274
- _proto.checkMissedUpdates = function checkMissedUpdates(fragmentResult) {
504
+ _proto2.checkMissedUpdates = function checkMissedUpdates(fragmentResult) {
275
505
  var environment = this._environment;
276
- var cacheKey = fragmentResult.cacheKey;
277
506
  var renderedSnapshot = fragmentResult.snapshot;
278
507
 
279
508
  if (!renderedSnapshot) {
280
509
  return [false, null];
281
510
  }
282
511
 
283
- var didMissUpdates = false;
512
+ var storeEpoch = null; // Bail out if the store hasn't been written since last read
513
+
514
+ storeEpoch = environment.getStore().getEpoch();
515
+
516
+ if (fragmentResult.storeEpoch === storeEpoch) {
517
+ return [false, fragmentResult.snapshot];
518
+ }
519
+
520
+ var cacheKey = fragmentResult.cacheKey;
284
521
 
285
522
  if (Array.isArray(renderedSnapshot)) {
523
+ var didMissUpdates = false;
286
524
  var currentSnapshots = [];
287
525
  renderedSnapshot.forEach(function (snapshot, idx) {
288
526
  var currentSnapshot = environment.lookup(snapshot.selector);
@@ -298,10 +536,14 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
298
536
  }
299
537
 
300
538
  currentSnapshots[idx] = currentSnapshot;
301
- });
539
+ }); // Only update the cache when the data is changed to avoid
540
+ // returning different `data` instances
302
541
 
303
542
  if (didMissUpdates) {
304
- this._cache.set(cacheKey, getFragmentResult(cacheKey, currentSnapshots));
543
+ this._cache.set(cacheKey, {
544
+ kind: 'done',
545
+ result: getFragmentResult(cacheKey, currentSnapshots, storeEpoch)
546
+ });
305
547
  }
306
548
 
307
549
  return [didMissUpdates, currentSnapshots];
@@ -311,63 +553,71 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
311
553
  var renderData = renderedSnapshot.data;
312
554
  var currentData = currentSnapshot.data;
313
555
  var updatedData = recycleNodesInto(renderData, currentData);
314
- currentSnapshot = {
556
+ var updatedCurrentSnapshot = {
315
557
  data: updatedData,
316
558
  isMissingData: currentSnapshot.isMissingData,
559
+ missingClientEdges: currentSnapshot.missingClientEdges,
317
560
  seenRecords: currentSnapshot.seenRecords,
318
561
  selector: currentSnapshot.selector,
319
562
  missingRequiredFields: currentSnapshot.missingRequiredFields
320
563
  };
321
564
 
322
565
  if (updatedData !== renderData) {
323
- this._cache.set(cacheKey, getFragmentResult(cacheKey, currentSnapshot));
324
-
325
- didMissUpdates = true;
566
+ this._cache.set(cacheKey, {
567
+ kind: 'done',
568
+ result: getFragmentResult(cacheKey, updatedCurrentSnapshot, storeEpoch)
569
+ });
326
570
  }
327
571
 
328
- return [didMissUpdates, currentSnapshot];
572
+ return [updatedData !== renderData, updatedCurrentSnapshot];
329
573
  };
330
574
 
331
- _proto.checkMissedUpdatesSpec = function checkMissedUpdatesSpec(fragmentResults) {
332
- var _this4 = this;
575
+ _proto2.checkMissedUpdatesSpec = function checkMissedUpdatesSpec(fragmentResults) {
576
+ var _this7 = this;
333
577
 
334
578
  return Object.keys(fragmentResults).some(function (key) {
335
- return _this4.checkMissedUpdates(fragmentResults[key])[0];
579
+ return _this7.checkMissedUpdates(fragmentResults[key])[0];
336
580
  });
337
581
  };
338
582
 
339
- _proto._getAndSavePromiseForFragmentRequestInFlight = function _getAndSavePromiseForFragmentRequestInFlight(cacheKey, fragmentNode, fragmentOwner) {
340
- var _this5 = this;
583
+ _proto2._getAndSavePromiseForFragmentRequestInFlight = function _getAndSavePromiseForFragmentRequestInFlight(cacheKey, fragmentNode, fragmentOwner, fragmentResult) {
584
+ var _this8 = this;
341
585
 
342
- var _getPromiseForActiveR;
586
+ var pendingOperationsResult = getPendingOperationsForFragment(this._environment, fragmentNode, fragmentOwner);
343
587
 
344
- var environment = this._environment;
345
- var networkPromise = (_getPromiseForActiveR = getPromiseForActiveRequest(environment, fragmentOwner)) !== null && _getPromiseForActiveR !== void 0 ? _getPromiseForActiveR : getPromiseForPendingOperationAffectingOwner(environment, fragmentOwner);
346
-
347
- if (!networkPromise) {
588
+ if (pendingOperationsResult == null) {
348
589
  return null;
349
590
  } // When the Promise for the request resolves, we need to make sure to
350
591
  // update the cache with the latest data available in the store before
351
592
  // resolving the Promise
352
593
 
353
594
 
595
+ var networkPromise = pendingOperationsResult.promise;
596
+ var pendingOperations = pendingOperationsResult.pendingOperations;
354
597
  var promise = networkPromise.then(function () {
355
- _this5._cache["delete"](cacheKey);
598
+ _this8._cache["delete"](cacheKey);
356
599
  })["catch"](function (error) {
357
- _this5._cache["delete"](cacheKey);
358
- });
600
+ _this8._cache["delete"](cacheKey);
601
+ }); // $FlowExpectedError[prop-missing] Expando to annotate Promises.
359
602
 
360
- this._cache.set(cacheKey, promise);
603
+ promise.displayName = networkPromise.displayName;
361
604
 
362
- var queryName = fragmentOwner.node.params.name;
363
- var fragmentName = fragmentNode.name;
364
- var promiseDisplayName = queryName === fragmentName ? "Relay(".concat(queryName, ")") : "Relay(".concat(queryName, ":").concat(fragmentName, ")"); // $FlowExpectedError[prop-missing] Expando to annotate Promises.
605
+ this._cache.set(cacheKey, {
606
+ kind: 'pending',
607
+ pendingOperations: pendingOperations,
608
+ promise: promise,
609
+ result: fragmentResult
610
+ });
365
611
 
366
- promise.displayName = promiseDisplayName;
367
- return promise;
612
+ return {
613
+ promise: promise,
614
+ pendingOperations: pendingOperations
615
+ };
368
616
  };
369
617
 
370
- _proto._updatePluralSnapshot = function _updatePluralSnapshot(cacheKey, baseSnapshots, latestSnapshot, idx) {
618
+ _proto2._updatePluralSnapshot = function _updatePluralSnapshot(cacheKey, baseSnapshots, latestSnapshot, idx, storeEpoch) {
619
+ var _currentFragmentResul;
620
+
371
621
  var currentFragmentResult = this._cache.get(cacheKey);
372
622
 
373
623
  if (isPromise(currentFragmentResult)) {
@@ -375,7 +625,7 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
375
625
  return;
376
626
  }
377
627
 
378
- var currentSnapshot = currentFragmentResult === null || currentFragmentResult === void 0 ? void 0 : currentFragmentResult.snapshot;
628
+ var currentSnapshot = currentFragmentResult === null || currentFragmentResult === void 0 ? void 0 : (_currentFragmentResul = currentFragmentResult.result) === null || _currentFragmentResul === void 0 ? void 0 : _currentFragmentResul.snapshot;
379
629
 
380
630
  if (currentSnapshot && !Array.isArray(currentSnapshot)) {
381
631
  reportInvalidCachedData(latestSnapshot.selector.node.name);
@@ -385,7 +635,10 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
385
635
  var nextSnapshots = currentSnapshot ? (0, _toConsumableArray2["default"])(currentSnapshot) : (0, _toConsumableArray2["default"])(baseSnapshots);
386
636
  nextSnapshots[idx] = latestSnapshot;
387
637
 
388
- this._cache.set(cacheKey, getFragmentResult(cacheKey, nextSnapshots));
638
+ this._cache.set(cacheKey, {
639
+ kind: 'done',
640
+ result: getFragmentResult(cacheKey, nextSnapshots, storeEpoch)
641
+ });
389
642
  };
390
643
 
391
644
  return FragmentResourceImpl;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.