react-relay 11.0.2 → 13.0.0-rc.2

Sign up to get free protection for your applications and to get access to all the features.
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.