react-relay 13.1.1 → 14.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (178) hide show
  1. package/ReactRelayContainerUtils.js.flow +0 -2
  2. package/ReactRelayContext.js +1 -1
  3. package/ReactRelayContext.js.flow +0 -2
  4. package/ReactRelayFragmentContainer.js.flow +7 -6
  5. package/ReactRelayFragmentMockRenderer.js.flow +0 -2
  6. package/ReactRelayLocalQueryRenderer.js.flow +1 -3
  7. package/ReactRelayPaginationContainer.js.flow +13 -10
  8. package/ReactRelayQueryFetcher.js.flow +10 -11
  9. package/ReactRelayQueryRenderer.js.flow +15 -16
  10. package/ReactRelayQueryRendererContext.js.flow +1 -3
  11. package/ReactRelayRefetchContainer.js.flow +10 -7
  12. package/ReactRelayTestMocker.js.flow +0 -2
  13. package/ReactRelayTypes.js.flow +6 -8
  14. package/RelayContext.js.flow +0 -2
  15. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +2 -4
  16. package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +3 -5
  17. package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +3 -5
  18. package/__flowtests__/RelayModern-flowtest.js.flow +2 -4
  19. package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +2 -4
  20. package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +2 -4
  21. package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +2 -4
  22. package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +2 -4
  23. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +2 -2
  24. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +2 -2
  25. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +3 -3
  26. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +3 -3
  27. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +3 -3
  28. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +3 -3
  29. package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +2 -2
  30. package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +2 -2
  31. package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +2 -2
  32. package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +2 -2
  33. package/assertFragmentMap.js.flow +0 -2
  34. package/buildReactRelayContainer.js.flow +2 -4
  35. package/getRootVariablesForFragments.js.flow +0 -2
  36. package/hooks.js +1 -1
  37. package/hooks.js.flow +0 -2
  38. package/index.js +1 -1
  39. package/index.js.flow +2 -2
  40. package/isRelayEnvironment.js.flow +0 -2
  41. package/jest-react/internalAct.js.flow +25 -9
  42. package/legacy.js +1 -1
  43. package/legacy.js.flow +0 -2
  44. package/lib/ReactRelayContainerUtils.js +0 -1
  45. package/lib/ReactRelayContext.js +0 -1
  46. package/lib/ReactRelayFragmentContainer.js +10 -9
  47. package/lib/ReactRelayFragmentMockRenderer.js +0 -1
  48. package/lib/ReactRelayLocalQueryRenderer.js +0 -1
  49. package/lib/ReactRelayPaginationContainer.js +14 -11
  50. package/lib/ReactRelayQueryFetcher.js +2 -2
  51. package/lib/ReactRelayQueryRenderer.js +2 -4
  52. package/lib/ReactRelayQueryRendererContext.js +0 -1
  53. package/lib/ReactRelayRefetchContainer.js +11 -14
  54. package/lib/ReactRelayTestMocker.js +1 -2
  55. package/lib/ReactRelayTypes.js +0 -1
  56. package/lib/RelayContext.js +0 -1
  57. package/lib/assertFragmentMap.js +0 -1
  58. package/lib/buildReactRelayContainer.js +1 -2
  59. package/lib/getRootVariablesForFragments.js +1 -2
  60. package/lib/hooks.js +0 -1
  61. package/lib/index.js +3 -1
  62. package/lib/isRelayEnvironment.js +0 -1
  63. package/lib/jest-react/internalAct.js +24 -4
  64. package/lib/legacy.js +0 -1
  65. package/lib/multi-actor/useRelayActorEnvironment.js +0 -1
  66. package/lib/readContext.js +2 -2
  67. package/lib/relay-hooks/EntryPointContainer.react.js +0 -1
  68. package/lib/relay-hooks/EntryPointTypes.flow.js +0 -1
  69. package/lib/relay-hooks/FragmentResource.js +68 -29
  70. package/lib/relay-hooks/HooksImplementation.js +29 -0
  71. package/lib/relay-hooks/InternalLogger.js +0 -1
  72. package/lib/relay-hooks/LRUCache.js +0 -1
  73. package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +0 -1
  74. package/lib/relay-hooks/MatchContainer.js +2 -2
  75. package/lib/relay-hooks/ProfilerContext.js +0 -1
  76. package/lib/relay-hooks/QueryResource.js +5 -168
  77. package/lib/relay-hooks/RelayEnvironmentProvider.js +0 -1
  78. package/lib/relay-hooks/SuspenseResource.js +1 -2
  79. package/lib/relay-hooks/loadQuery.js +1 -1
  80. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +8 -13
  81. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +0 -1
  82. package/lib/relay-hooks/react-cache/RelayReactCache.js +36 -0
  83. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +344 -0
  84. package/lib/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js +239 -0
  85. package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +598 -0
  86. package/lib/relay-hooks/react-cache/useFragment_REACT_CACHE.js +50 -0
  87. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +55 -0
  88. package/lib/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js +150 -0
  89. package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +124 -0
  90. package/lib/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js +367 -0
  91. package/lib/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js +45 -0
  92. package/lib/relay-hooks/useBlockingPaginationFragment.js +4 -3
  93. package/lib/relay-hooks/useClientQuery.js +33 -0
  94. package/lib/relay-hooks/useEntryPointLoader.js +1 -2
  95. package/lib/relay-hooks/useFetchTrackingRef.js +0 -1
  96. package/lib/relay-hooks/useFragment.js +15 -2
  97. package/lib/relay-hooks/useFragmentNode.js +0 -1
  98. package/lib/relay-hooks/useIsMountedRef.js +0 -1
  99. package/lib/relay-hooks/useLazyLoadQuery.js +4 -2
  100. package/lib/relay-hooks/useLazyLoadQueryNode.js +0 -1
  101. package/lib/relay-hooks/useLoadMoreFunction.js +1 -2
  102. package/lib/relay-hooks/useMemoOperationDescriptor.js +0 -1
  103. package/lib/relay-hooks/useMemoVariables.js +0 -1
  104. package/lib/relay-hooks/useMutation.js +5 -7
  105. package/lib/relay-hooks/usePaginationFragment.js +15 -3
  106. package/lib/relay-hooks/usePreloadedQuery.js +4 -2
  107. package/lib/relay-hooks/useQueryLoader.js +1 -2
  108. package/lib/relay-hooks/useRefetchableFragment.js +14 -2
  109. package/lib/relay-hooks/useRefetchableFragmentNode.js +1 -2
  110. package/lib/relay-hooks/useRelayEnvironment.js +0 -1
  111. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +0 -1
  112. package/lib/relay-hooks/useSubscribeToInvalidationState.js +0 -1
  113. package/lib/relay-hooks/useSubscription.js +0 -1
  114. package/multi-actor/useRelayActorEnvironment.js.flow +0 -2
  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 -2
  123. package/relay-hooks/EntryPointContainer.react.js.flow +2 -4
  124. package/relay-hooks/EntryPointTypes.flow.js.flow +30 -32
  125. package/relay-hooks/FragmentResource.js.flow +80 -37
  126. package/relay-hooks/HooksImplementation.js.flow +43 -0
  127. package/relay-hooks/InternalLogger.js.flow +0 -2
  128. package/relay-hooks/LRUCache.js.flow +0 -2
  129. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +4 -6
  130. package/relay-hooks/MatchContainer.js.flow +11 -6
  131. package/relay-hooks/ProfilerContext.js.flow +0 -2
  132. package/relay-hooks/QueryResource.js.flow +12 -209
  133. package/relay-hooks/RelayEnvironmentProvider.js.flow +2 -4
  134. package/relay-hooks/SuspenseResource.js.flow +0 -2
  135. package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +3 -3
  136. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +2 -2
  137. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +2 -2
  138. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +2 -2
  139. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +4 -6
  140. package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +0 -2
  141. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +4 -6
  142. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +0 -2
  143. package/relay-hooks/__flowtests__/utils.js.flow +8 -10
  144. package/relay-hooks/loadQuery.js.flow +2 -1
  145. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +11 -20
  146. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +0 -2
  147. package/relay-hooks/react-cache/RelayReactCache.js.flow +40 -0
  148. package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +430 -0
  149. package/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js.flow +297 -0
  150. package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +599 -0
  151. package/relay-hooks/react-cache/useFragment_REACT_CACHE.js.flow +72 -0
  152. package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +70 -0
  153. package/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js.flow +171 -0
  154. package/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js.flow +151 -0
  155. package/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow +595 -0
  156. package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +65 -0
  157. package/relay-hooks/useBlockingPaginationFragment.js.flow +4 -6
  158. package/relay-hooks/useClientQuery.js.flow +39 -0
  159. package/relay-hooks/useEntryPointLoader.js.flow +6 -8
  160. package/relay-hooks/useFetchTrackingRef.js.flow +2 -4
  161. package/relay-hooks/useFragment.js.flow +17 -12
  162. package/relay-hooks/useFragmentNode.js.flow +2 -4
  163. package/relay-hooks/useIsMountedRef.js.flow +1 -3
  164. package/relay-hooks/useLazyLoadQuery.js.flow +17 -5
  165. package/relay-hooks/useLazyLoadQueryNode.js.flow +2 -4
  166. package/relay-hooks/useLoadMoreFunction.js.flow +6 -8
  167. package/relay-hooks/useMemoOperationDescriptor.js.flow +0 -2
  168. package/relay-hooks/useMemoVariables.js.flow +0 -2
  169. package/relay-hooks/useMutation.js.flow +5 -7
  170. package/relay-hooks/usePaginationFragment.js.flow +44 -19
  171. package/relay-hooks/usePreloadedQuery.js.flow +14 -5
  172. package/relay-hooks/useQueryLoader.js.flow +4 -6
  173. package/relay-hooks/useRefetchableFragment.js.flow +32 -3
  174. package/relay-hooks/useRefetchableFragmentNode.js.flow +38 -25
  175. package/relay-hooks/useRelayEnvironment.js.flow +0 -2
  176. package/relay-hooks/useStaticFragmentNodeWarning.js.flow +0 -2
  177. package/relay-hooks/useSubscribeToInvalidationState.js.flow +0 -2
  178. package/relay-hooks/useSubscription.js.flow +14 -10
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @emails oncall+relay
9
+ * @format
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {
15
+ FetchPolicy,
16
+ GraphQLResponse,
17
+ IEnvironment,
18
+ Observable,
19
+ OperationDescriptor,
20
+ ReaderFragment,
21
+ RenderPolicy,
22
+ } from 'relay-runtime';
23
+
24
+ const SuspenseResource = require('../SuspenseResource');
25
+ const {getCacheForType, getCacheSignal} = require('./RelayReactCache');
26
+ const invariant = require('invariant');
27
+ const {
28
+ RelayFeatureFlags,
29
+ __internal: {fetchQuery: fetchQueryInternal},
30
+ } = require('relay-runtime');
31
+ const warning = require('warning');
32
+
33
+ type QueryCacheCommitable = () => () => void;
34
+
35
+ type QueryResult = {
36
+ fragmentNode: ReaderFragment,
37
+ fragmentRef: mixed,
38
+ };
39
+
40
+ // Note that the status of a cache entry will be 'resolved' when partial
41
+ // rendering is allowed, even if a fetch is ongoing. The pending status
42
+ // is specifically to indicate that we should suspend.
43
+ // Note also that the retainCount is different from the retain count of
44
+ // an operation, which is maintained by the Environment. This retain
45
+ // count is used in Legacy Timeouts mode to count how many components
46
+ // are mounted that use the entry, plus one count for the temporary retain
47
+ // before any components have mounted. It is unused when Legacy Timeouts
48
+ // mode is off.
49
+ type QueryCacheEntryStatus =
50
+ | {
51
+ status: 'resolved',
52
+ result: QueryResult,
53
+ }
54
+ | {
55
+ status: 'pending',
56
+ promise: Promise<void>,
57
+ }
58
+ | {
59
+ status: 'rejected',
60
+ error: Error,
61
+ };
62
+
63
+ type QueryCacheEntry = {
64
+ ...QueryCacheEntryStatus,
65
+ onCommit: QueryCacheCommitable,
66
+ suspenseResource: SuspenseResource | null,
67
+ };
68
+
69
+ const DEFAULT_FETCH_POLICY = 'store-or-network';
70
+
71
+ const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
72
+
73
+ interface IMap<K, V> {
74
+ delete(key: K): boolean;
75
+ get(key: K): V | void;
76
+ set(key: K, value: V): IMap<K, V>;
77
+ }
78
+
79
+ type QueryCacheKey = string;
80
+
81
+ class QueryCache {
82
+ _map: IMap<IEnvironment, Map<QueryCacheKey, QueryCacheEntry>>;
83
+
84
+ constructor() {
85
+ this._map = WEAKMAP_SUPPORTED ? new WeakMap() : new Map();
86
+ }
87
+
88
+ get(environment: IEnvironment, key: QueryCacheKey): QueryCacheEntry | void {
89
+ let forEnv = this._map.get(environment);
90
+ if (!forEnv) {
91
+ forEnv = new Map();
92
+ this._map.set(environment, forEnv);
93
+ }
94
+ return forEnv.get(key);
95
+ }
96
+
97
+ set(
98
+ environment: IEnvironment,
99
+ key: QueryCacheKey,
100
+ value: QueryCacheEntry,
101
+ ): void {
102
+ let forEnv = this._map.get(environment);
103
+ if (!forEnv) {
104
+ forEnv = new Map();
105
+ this._map.set(environment, forEnv);
106
+ }
107
+ forEnv.set(key, value);
108
+ }
109
+
110
+ delete(environment: IEnvironment, key: QueryCacheKey): void {
111
+ const forEnv = this._map.get(environment);
112
+ if (!forEnv) {
113
+ return;
114
+ }
115
+ forEnv.delete(key);
116
+ if (forEnv.size === 0) {
117
+ this._map.delete(environment);
118
+ }
119
+ }
120
+ }
121
+
122
+ function createQueryCache(): QueryCache {
123
+ return new QueryCache();
124
+ }
125
+
126
+ const noopOnCommit = () => {
127
+ return () => undefined;
128
+ };
129
+
130
+ const noopPromise = new Promise(() => {});
131
+
132
+ function getQueryCacheKey(
133
+ operation: OperationDescriptor,
134
+ fetchPolicy: FetchPolicy,
135
+ renderPolicy: RenderPolicy,
136
+ fetchKey?: ?string | ?number,
137
+ ): QueryCacheKey {
138
+ return `${fetchPolicy}-${renderPolicy}-${operation.request.identifier}-${
139
+ fetchKey ?? ''
140
+ }`;
141
+ }
142
+
143
+ function constructQueryResult(operation: OperationDescriptor): QueryResult {
144
+ const rootFragmentRef = {
145
+ __id: operation.fragment.dataID,
146
+ __fragments: {
147
+ [operation.fragment.node.name]: operation.request.variables,
148
+ },
149
+ __fragmentOwner: operation.request,
150
+ };
151
+ return {
152
+ fragmentNode: operation.request.node.fragment,
153
+ fragmentRef: rootFragmentRef,
154
+ };
155
+ }
156
+
157
+ function makeInitialCacheEntry() {
158
+ return {
159
+ status: 'pending',
160
+ promise: noopPromise,
161
+ onCommit: noopOnCommit,
162
+ suspenseResource: null,
163
+ };
164
+ }
165
+
166
+ function getQueryResultOrFetchQuery_REACT_CACHE(
167
+ environment: IEnvironment,
168
+ queryOperationDescriptor: OperationDescriptor,
169
+ options?: {
170
+ fetchPolicy?: FetchPolicy,
171
+ renderPolicy?: RenderPolicy,
172
+ fetchKey?: ?string | ?number,
173
+ fetchObservable?: Observable<GraphQLResponse>,
174
+ },
175
+ ): [QueryResult, QueryCacheCommitable] {
176
+ const fetchPolicy = options?.fetchPolicy ?? DEFAULT_FETCH_POLICY;
177
+ const renderPolicy =
178
+ options?.renderPolicy ?? environment.UNSTABLE_getDefaultRenderPolicy();
179
+
180
+ const cache = getCacheForType(createQueryCache);
181
+
182
+ const cacheKey = getQueryCacheKey(
183
+ queryOperationDescriptor,
184
+ fetchPolicy,
185
+ renderPolicy,
186
+ options?.fetchKey,
187
+ );
188
+
189
+ const initialEntry = cache.get(environment, cacheKey);
190
+
191
+ function updateCache(
192
+ updater: QueryCacheEntryStatus => QueryCacheEntryStatus,
193
+ ) {
194
+ let currentEntry = cache.get(environment, cacheKey);
195
+ if (!currentEntry) {
196
+ currentEntry = makeInitialCacheEntry();
197
+ cache.set(environment, cacheKey, currentEntry);
198
+ }
199
+ // $FlowExpectedError[prop-missing] Extra properties are passed in -- this is fine
200
+ const newStatus: {...} = updater(currentEntry);
201
+ // $FlowExpectedError[cannot-spread-inexact] Flow cannot understand that this is valid...
202
+ cache.set(environment, cacheKey, {...currentEntry, ...newStatus});
203
+ // ... but we can because QueryCacheEntry spreads QueryCacheEntryStatus, so spreading
204
+ // a QueryCacheEntryStatus into a QueryCacheEntry will result in a valid QueryCacheEntry.
205
+ }
206
+
207
+ // Initiate a query to fetch the data if needed:
208
+ if (RelayFeatureFlags.USE_REACT_CACHE_LEGACY_TIMEOUTS) {
209
+ let entry;
210
+ if (initialEntry === undefined) {
211
+ onCacheMiss(
212
+ environment,
213
+ queryOperationDescriptor,
214
+ fetchPolicy,
215
+ renderPolicy,
216
+ updateCache,
217
+ options?.fetchObservable,
218
+ );
219
+ const createdEntry = cache.get(environment, cacheKey);
220
+ invariant(
221
+ createdEntry !== undefined,
222
+ 'An entry should have been created by onCacheMiss. This is a bug in Relay.',
223
+ );
224
+ entry = createdEntry;
225
+ } else {
226
+ entry = initialEntry;
227
+ }
228
+ if (!entry.suspenseResource) {
229
+ entry.suspenseResource = new SuspenseResource(() => {
230
+ const retention = environment.retain(queryOperationDescriptor);
231
+ return {
232
+ dispose: () => {
233
+ retention.dispose();
234
+ cache.delete(environment, cacheKey);
235
+ },
236
+ };
237
+ });
238
+ }
239
+ if (entry.onCommit === noopOnCommit) {
240
+ entry.onCommit = () => {
241
+ invariant(
242
+ entry.suspenseResource,
243
+ 'SuspenseResource should have been initialized. This is a bug in Relay.',
244
+ );
245
+ const retention = entry.suspenseResource.permanentRetain(environment);
246
+ return () => {
247
+ retention.dispose();
248
+ };
249
+ };
250
+ }
251
+ entry.suspenseResource.temporaryRetain(environment);
252
+ } else {
253
+ if (initialEntry === undefined) {
254
+ // This is the behavior we eventually want: We retain the query until the
255
+ // presiding Cache component unmounts, at which point the AbortSignal
256
+ // will be triggered.
257
+ onCacheMiss(
258
+ environment,
259
+ queryOperationDescriptor,
260
+ fetchPolicy,
261
+ renderPolicy,
262
+ updateCache,
263
+ options?.fetchObservable,
264
+ );
265
+
266
+ // Since this is the first time rendering, retain the query. React will
267
+ // trigger the abort signal when this cache entry is no longer needed.
268
+ const retention = environment.retain(queryOperationDescriptor);
269
+
270
+ const dispose = () => {
271
+ retention.dispose();
272
+ cache.delete(environment, cacheKey);
273
+ };
274
+ const abortSignal = getCacheSignal();
275
+ abortSignal.addEventListener('abort', dispose, {once: true});
276
+ }
277
+ }
278
+
279
+ const entry = cache.get(environment, cacheKey); // could be a different entry now if synchronously resolved
280
+ invariant(
281
+ entry !== undefined,
282
+ 'An entry should have been created by onCacheMiss. This is a bug in Relay.',
283
+ );
284
+ switch (entry.status) {
285
+ case 'pending':
286
+ throw entry.promise;
287
+ case 'rejected':
288
+ throw entry.error;
289
+ case 'resolved':
290
+ return [entry.result, entry.onCommit];
291
+ }
292
+ invariant(false, 'switch statement should be exhaustive');
293
+ }
294
+
295
+ function onCacheMiss(
296
+ environment: IEnvironment,
297
+ operation: OperationDescriptor,
298
+ fetchPolicy: FetchPolicy,
299
+ renderPolicy: RenderPolicy,
300
+ updateCache: ((QueryCacheEntryStatus) => QueryCacheEntryStatus) => void,
301
+ customFetchObservable?: Observable<GraphQLResponse>,
302
+ ): void {
303
+ // NB: Besides checking if the data is available, calling `check` will write missing
304
+ // data to the store using any missing data handlers specified in the environment.
305
+ const queryAvailability = environment.check(operation);
306
+ const queryStatus = queryAvailability.status;
307
+ const hasFullQuery = queryStatus === 'available';
308
+ const canPartialRender =
309
+ hasFullQuery || (renderPolicy === 'partial' && queryStatus !== 'stale');
310
+
311
+ let shouldFetch;
312
+ let shouldRenderNow;
313
+ switch (fetchPolicy) {
314
+ case 'store-only': {
315
+ shouldFetch = false;
316
+ shouldRenderNow = true;
317
+ break;
318
+ }
319
+ case 'store-or-network': {
320
+ shouldFetch = !hasFullQuery;
321
+ shouldRenderNow = canPartialRender;
322
+ break;
323
+ }
324
+ case 'store-and-network': {
325
+ shouldFetch = true;
326
+ shouldRenderNow = canPartialRender;
327
+ break;
328
+ }
329
+ case 'network-only':
330
+ default: {
331
+ shouldFetch = true;
332
+ shouldRenderNow = false;
333
+ break;
334
+ }
335
+ }
336
+
337
+ if (shouldFetch) {
338
+ executeOperationAndKeepUpToDate(
339
+ environment,
340
+ operation,
341
+ updateCache,
342
+ customFetchObservable,
343
+ );
344
+ updateCache(existing => {
345
+ switch (existing.status) {
346
+ case 'resolved':
347
+ return existing;
348
+ case 'rejected':
349
+ return existing;
350
+ case 'pending':
351
+ return shouldRenderNow
352
+ ? {
353
+ status: 'resolved',
354
+ result: constructQueryResult(operation),
355
+ }
356
+ : existing;
357
+ }
358
+ });
359
+ } else {
360
+ invariant(
361
+ shouldRenderNow,
362
+ 'Should either fetch or be willing to render. This is a bug in Relay.',
363
+ );
364
+ updateCache(_existing => ({
365
+ status: 'resolved',
366
+ result: constructQueryResult(operation),
367
+ }));
368
+ }
369
+ }
370
+
371
+ function executeOperationAndKeepUpToDate(
372
+ environment: IEnvironment,
373
+ operation: OperationDescriptor,
374
+ updateCache: ((QueryCacheEntryStatus) => QueryCacheEntryStatus) => void,
375
+ customFetchObservable?: Observable<GraphQLResponse>,
376
+ ) {
377
+ let resolvePromise;
378
+ const promise = new Promise(r => {
379
+ resolvePromise = r;
380
+ });
381
+ // $FlowExpectedError[prop-missing] Expando to annotate Promises.
382
+ promise.displayName = 'Relay(' + operation.request.node.operation.name + ')';
383
+
384
+ let isFirstPayload = true;
385
+
386
+ // FIXME We may still need to cancel network requests for live queries.
387
+ const fetchObservable =
388
+ customFetchObservable ?? fetchQueryInternal(environment, operation);
389
+ fetchObservable.subscribe({
390
+ start: subscription => {},
391
+ error: error => {
392
+ if (isFirstPayload) {
393
+ updateCache(_existing => ({
394
+ status: 'rejected',
395
+ error,
396
+ }));
397
+ } else {
398
+ // TODO:T92030819 Remove this warning and actually throw the network error
399
+ // To complete this task we need to have a way of precisely tracking suspendable points
400
+ warning(
401
+ false,
402
+ 'getQueryResultOrFetchQuery: An incremental payload for query `%` returned an error: `%`:`%`.',
403
+ operation.request.node.operation.name,
404
+ error.message,
405
+ error.stack,
406
+ );
407
+ }
408
+ resolvePromise();
409
+ isFirstPayload = false;
410
+ },
411
+ next: response => {
412
+ // Stop suspending on the first payload because of streaming, defer, etc.
413
+ updateCache(_existing => ({
414
+ status: 'resolved',
415
+ result: constructQueryResult(operation),
416
+ }));
417
+ resolvePromise();
418
+ isFirstPayload = false;
419
+ },
420
+ });
421
+
422
+ // If the above subscription yields a value synchronously, then one of the updates
423
+ // above will have already happened and we'll now be in a resolved or rejected state.
424
+ // But in the usual case, we save the promise to the entry here:
425
+ updateCache(existing =>
426
+ existing.status === 'pending' ? {status: 'pending', promise} : existing,
427
+ );
428
+ }
429
+
430
+ module.exports = getQueryResultOrFetchQuery_REACT_CACHE;