@apollo/client 4.3.0-alpha.4 → 4.3.0-alpha.5

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 (150) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +3 -7
  3. package/__cjs/cache/core/cache.cjs +1 -1
  4. package/__cjs/cache/core/cache.cjs.map +1 -1
  5. package/__cjs/cache/core/cache.d.cts +5 -1
  6. package/__cjs/cache/inmemory/entityStore.cjs +3 -3
  7. package/__cjs/cache/inmemory/inMemoryCache.cjs +0 -4
  8. package/__cjs/cache/inmemory/inMemoryCache.cjs.map +1 -1
  9. package/__cjs/cache/inmemory/inMemoryCache.d.cts +3 -4
  10. package/__cjs/cache/inmemory/key-extractor.cjs +1 -1
  11. package/__cjs/cache/inmemory/policies.cjs +4 -4
  12. package/__cjs/cache/inmemory/readFromStore.cjs +71 -54
  13. package/__cjs/cache/inmemory/readFromStore.cjs.map +1 -1
  14. package/__cjs/cache/inmemory/readFromStore.d.cts +1 -1
  15. package/__cjs/cache/inmemory/types.d.cts +1 -2
  16. package/__cjs/cache/inmemory/writeToStore.cjs +4 -4
  17. package/__cjs/core/ApolloClient.cjs +21 -22
  18. package/__cjs/core/ApolloClient.cjs.map +1 -1
  19. package/__cjs/core/ApolloClient.d.cts +44 -10
  20. package/__cjs/core/ObservableQuery.cjs +46 -5
  21. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  22. package/__cjs/core/ObservableQuery.d.cts +12 -1
  23. package/__cjs/core/QueryInfo.cjs +177 -154
  24. package/__cjs/core/QueryInfo.cjs.map +1 -1
  25. package/__cjs/core/QueryInfo.d.cts +13 -3
  26. package/__cjs/core/QueryManager.cjs +49 -27
  27. package/__cjs/core/QueryManager.cjs.map +1 -1
  28. package/__cjs/core/RefetchEventManager.cjs +3 -3
  29. package/__cjs/core/dataStateErrorCache.cjs +12 -0
  30. package/__cjs/core/dataStateErrorCache.cjs.map +1 -0
  31. package/__cjs/core/dataStateErrorCache.d.cts +3 -0
  32. package/__cjs/core/types.d.cts +2 -0
  33. package/__cjs/incremental/handlers/graphql17Alpha9.cjs +34 -11
  34. package/__cjs/incremental/handlers/graphql17Alpha9.cjs.map +1 -1
  35. package/__cjs/incremental/handlers/graphql17Alpha9.d.cts +26 -3
  36. package/__cjs/incremental/types.d.cts +41 -3
  37. package/__cjs/invariantErrorCodes.cjs +50 -31
  38. package/__cjs/react/hooks/useBackgroundQuery.cjs.map +1 -1
  39. package/__cjs/react/hooks/useBackgroundQuery.d.cts +100 -47
  40. package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
  41. package/__cjs/react/hooks/useLazyQuery.d.cts +26 -20
  42. package/__cjs/react/hooks/useLoadableQuery.cjs.map +1 -1
  43. package/__cjs/react/hooks/useLoadableQuery.d.cts +23 -15
  44. package/__cjs/react/hooks/useMutation.cjs.map +1 -1
  45. package/__cjs/react/hooks/useMutation.d.cts +10 -7
  46. package/__cjs/react/hooks/useQuery.cjs.map +1 -1
  47. package/__cjs/react/hooks/useQuery.d.cts +74 -32
  48. package/__cjs/react/hooks/useQueryRefHandlers.cjs.map +1 -1
  49. package/__cjs/react/hooks/useSuspenseQuery.cjs.map +1 -1
  50. package/__cjs/react/hooks/useSuspenseQuery.d.cts +83 -34
  51. package/__cjs/react/internal/cache/QueryReference.cjs.map +1 -1
  52. package/__cjs/react/internal/types.d.cts +14 -3
  53. package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
  54. package/__cjs/react/query-preloader/createQueryPreloader.d.cts +20 -5
  55. package/__cjs/testing/core/mocking/mockLink.cjs +21 -1
  56. package/__cjs/testing/core/mocking/mockLink.cjs.map +1 -1
  57. package/__cjs/testing/core/mocking/mockLink.d.cts +1 -2
  58. package/__cjs/utilities/internal/addDeferFragmentLabels.cjs +33 -0
  59. package/__cjs/utilities/internal/addDeferFragmentLabels.cjs.map +1 -0
  60. package/__cjs/utilities/internal/addDeferFragmentLabels.d.cts +3 -0
  61. package/__cjs/utilities/internal/getDirectiveArgValue.cjs +19 -0
  62. package/__cjs/utilities/internal/getDirectiveArgValue.cjs.map +1 -0
  63. package/__cjs/utilities/internal/getDirectiveArgValue.d.cts +9 -0
  64. package/__cjs/utilities/internal/index.cjs +6 -2
  65. package/__cjs/utilities/internal/index.cjs.map +1 -1
  66. package/__cjs/utilities/internal/index.d.cts +2 -0
  67. package/__cjs/version.cjs +1 -1
  68. package/cache/core/cache.d.ts +5 -1
  69. package/cache/core/cache.js +1 -1
  70. package/cache/core/cache.js.map +1 -1
  71. package/cache/inmemory/entityStore.js +3 -3
  72. package/cache/inmemory/inMemoryCache.d.ts +3 -4
  73. package/cache/inmemory/inMemoryCache.js +1 -5
  74. package/cache/inmemory/inMemoryCache.js.map +1 -1
  75. package/cache/inmemory/key-extractor.js +1 -1
  76. package/cache/inmemory/policies.js +4 -4
  77. package/cache/inmemory/readFromStore.d.ts +1 -1
  78. package/cache/inmemory/readFromStore.js +72 -55
  79. package/cache/inmemory/readFromStore.js.map +1 -1
  80. package/cache/inmemory/types.d.ts +1 -2
  81. package/cache/inmemory/types.js.map +1 -1
  82. package/cache/inmemory/writeToStore.js +4 -4
  83. package/core/ApolloClient.d.ts +44 -10
  84. package/core/ApolloClient.js +21 -22
  85. package/core/ApolloClient.js.map +1 -1
  86. package/core/ObservableQuery.d.ts +12 -1
  87. package/core/ObservableQuery.js +46 -5
  88. package/core/ObservableQuery.js.map +1 -1
  89. package/core/QueryInfo.d.ts +13 -3
  90. package/core/QueryInfo.js +173 -154
  91. package/core/QueryInfo.js.map +1 -1
  92. package/core/QueryManager.js +49 -27
  93. package/core/QueryManager.js.map +1 -1
  94. package/core/RefetchEventManager.js +3 -3
  95. package/core/dataStateErrorCache.d.ts +3 -0
  96. package/core/dataStateErrorCache.js +9 -0
  97. package/core/dataStateErrorCache.js.map +1 -0
  98. package/core/types.d.ts +2 -0
  99. package/core/types.js.map +1 -1
  100. package/incremental/handlers/graphql17Alpha9.d.ts +26 -3
  101. package/incremental/handlers/graphql17Alpha9.js +35 -12
  102. package/incremental/handlers/graphql17Alpha9.js.map +1 -1
  103. package/incremental/types.d.ts +41 -3
  104. package/incremental/types.js.map +1 -1
  105. package/invariantErrorCodes.js +50 -31
  106. package/package.json +1 -1
  107. package/react/hooks/useBackgroundQuery.d.ts +100 -47
  108. package/react/hooks/useBackgroundQuery.js.map +1 -1
  109. package/react/hooks/useLazyQuery.d.ts +26 -20
  110. package/react/hooks/useLazyQuery.js.map +1 -1
  111. package/react/hooks/useLoadableQuery.d.ts +23 -15
  112. package/react/hooks/useLoadableQuery.js.map +1 -1
  113. package/react/hooks/useMutation.d.ts +10 -7
  114. package/react/hooks/useMutation.js.map +1 -1
  115. package/react/hooks/useQuery.d.ts +74 -32
  116. package/react/hooks/useQuery.js.map +1 -1
  117. package/react/hooks/useQueryRefHandlers.js.map +1 -1
  118. package/react/hooks/useSuspenseQuery.d.ts +83 -34
  119. package/react/hooks/useSuspenseQuery.js.map +1 -1
  120. package/react/hooks-compiled/useBackgroundQuery.d.ts +100 -47
  121. package/react/hooks-compiled/useBackgroundQuery.js.map +1 -1
  122. package/react/hooks-compiled/useLazyQuery.d.ts +26 -20
  123. package/react/hooks-compiled/useLazyQuery.js.map +1 -1
  124. package/react/hooks-compiled/useLoadableQuery.d.ts +23 -15
  125. package/react/hooks-compiled/useLoadableQuery.js.map +1 -1
  126. package/react/hooks-compiled/useMutation.d.ts +10 -7
  127. package/react/hooks-compiled/useMutation.js.map +1 -1
  128. package/react/hooks-compiled/useQuery.d.ts +74 -32
  129. package/react/hooks-compiled/useQuery.js.map +1 -1
  130. package/react/hooks-compiled/useQueryRefHandlers.js.map +1 -1
  131. package/react/hooks-compiled/useSuspenseQuery.d.ts +83 -34
  132. package/react/hooks-compiled/useSuspenseQuery.js.map +1 -1
  133. package/react/internal/cache/QueryReference.js.map +1 -1
  134. package/react/internal/types.d.ts +15 -4
  135. package/react/internal/types.js.map +1 -1
  136. package/react/query-preloader/createQueryPreloader.d.ts +20 -5
  137. package/react/query-preloader/createQueryPreloader.js.map +1 -1
  138. package/testing/core/mocking/mockLink.d.ts +1 -2
  139. package/testing/core/mocking/mockLink.js +21 -1
  140. package/testing/core/mocking/mockLink.js.map +1 -1
  141. package/utilities/internal/addDeferFragmentLabels.d.ts +3 -0
  142. package/utilities/internal/addDeferFragmentLabels.js +30 -0
  143. package/utilities/internal/addDeferFragmentLabels.js.map +1 -0
  144. package/utilities/internal/getDirectiveArgValue.d.ts +9 -0
  145. package/utilities/internal/getDirectiveArgValue.js +16 -0
  146. package/utilities/internal/getDirectiveArgValue.js.map +1 -0
  147. package/utilities/internal/index.d.ts +2 -0
  148. package/utilities/internal/index.js +2 -0
  149. package/utilities/internal/index.js.map +1 -1
  150. package/version.js +1 -1
@@ -372,7 +372,18 @@ export declare class ObservableQuery<TData = unknown, TVariables extends Operati
372
372
  /**
373
373
  * A function that helps you fetch the next set of results for a [paginated list field](https://www.apollographql.com/docs/react/pagination/core-api/).
374
374
  */
375
- fetchMore<TFetchData = TData, TFetchVars extends OperationVariables = TVariables>(options: ObservableQuery.FetchMoreOptions<TData, TVariables, TFetchData, TFetchVars>): Promise<ApolloClient.QueryResult<TFetchData>>;
375
+ fetchMore<TFetchData = TData, TFetchVars extends OperationVariables = TVariables, TErrorPolicy extends ErrorPolicy = "none">(options: ObservableQuery.FetchMoreOptions<TData, TVariables, TFetchData, TFetchVars> & {
376
+ /**
377
+ * Specifies how the query handles a response that returns both GraphQL errors and partial results.
378
+ *
379
+ * For details, see [GraphQL error policies](https://www.apollographql.com/docs/react/data/error-handling/#graphql-error-policies).
380
+ *
381
+ * The default value is `none`, meaning that the query result includes error details but not partial results.
382
+ *
383
+ * @docGroup 1. Operation options
384
+ */
385
+ errorPolicy?: TErrorPolicy;
386
+ }): Promise<ApolloClient.QueryResult<TFetchData, TErrorPolicy>>;
376
387
  /**
377
388
  * A function that enables you to execute a [subscription](https://www.apollographql.com/docs/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query.
378
389
  *
@@ -1,8 +1,13 @@
1
- "use strict";
1
+ "use strict";;
2
+ const {
3
+ __DEV__
4
+ } = require("@apollo/client/utilities/environment");
5
+
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
7
  exports.QueryInfo = void 0;
4
8
  const equality_1 = require("@wry/equality");
5
9
  const trie_1 = require("@wry/trie");
10
+ const environment_1 = require("@apollo/client/utilities/environment");
6
11
  const internal_1 = require("@apollo/client/utilities/internal");
7
12
  const invariant_1 = require("@apollo/client/utilities/invariant");
8
13
  const networkStatus_js_1 = require("./networkStatus.cjs");
@@ -44,10 +49,10 @@ class QueryInfo {
44
49
  this.observableQuery = observableQuery;
45
50
  this.queryManager = queryManager;
46
51
  // Track how often cache.evict is called, since we want eviction to
47
- // override the feud-stopping logic in the markQueryResult method, by
48
- // causing shouldWrite to return true. Wrapping the cache.evict method
49
- // is a bit of a hack, but it saves us from having to make eviction
50
- // counting an official part of the ApolloCache API.
52
+ // override the write-skipping logic in `shouldWrite`, by causing it to
53
+ // return true. Wrapping the cache.evict method is a bit of a hack, but it
54
+ // saves us from having to make eviction counting an official part of the
55
+ // ApolloCache API.
51
56
  if (!destructiveMethodCounts.has(cache)) {
52
57
  destructiveMethodCounts.set(cache, 0);
53
58
  wrapDestructiveCacheMethod(cache, "evict");
@@ -57,8 +62,14 @@ class QueryInfo {
57
62
  }
58
63
  /**
59
64
  * @internal
60
- * For feud-preventing behaviour, `lastWrite` should be shared by all `QueryInfo` instances of an `ObservableQuery`.
61
- * In the case of a standalone `QueryInfo`, we will keep a local version.
65
+ * Tracks the last result written to the cache so that `shouldWrite` can skip
66
+ * an identical write. Since a `QueryInfo` only ever represents a single
67
+ * network request, this is shared by all `QueryInfo` instances of an
68
+ * `ObservableQuery`. A standalone `QueryInfo` keeps a local version.
69
+ *
70
+ * A network result that was explicitly asked for always takes precedence over
71
+ * what is already cached, so `ObservableQuery.refetch` and polling clear this
72
+ * value before starting their request.
62
73
  *
63
74
  * @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
64
75
  */
@@ -74,33 +85,34 @@ class QueryInfo {
74
85
  }
75
86
  shouldWrite(result, variables) {
76
87
  const { lastWrite } = this;
77
- return !(lastWrite &&
88
+ return (!lastWrite ||
78
89
  // If cache.evict has been called since the last time we wrote this
79
90
  // data into the cache, there's a chance writing this result into
80
91
  // the cache will repair what was evicted.
81
- lastWrite.dmCount === destructiveMethodCounts.get(this.cache) &&
82
- (0, equality_1.equal)(variables, lastWrite.variables) &&
83
- (0, equality_1.equal)(result.data, lastWrite.result.data) &&
92
+ lastWrite.dmCount !== destructiveMethodCounts.get(this.cache) ||
93
+ !(0, equality_1.equal)(variables, lastWrite.variables) ||
94
+ !(0, equality_1.equal)(result.data, lastWrite.result.data) ||
84
95
  // We have to compare these values because its possible the final chunk
85
96
  // emitted in the incremental result is just `hasNext: false`. This
86
97
  // ensures we trigger a cache write when we get `isLastChunk: true`.
87
- result.extensions?.[internal_1.streamInfoSymbol] ===
88
- lastWrite.result.extensions?.[internal_1.streamInfoSymbol]);
98
+ lastWrite.hasNext !== this.hasNext);
89
99
  }
90
100
  get hasNext() {
91
101
  return this.incremental ? this.incremental.hasNext : false;
92
102
  }
103
+ get incrementalHandler() {
104
+ return this.queryManager.incrementalHandler;
105
+ }
93
106
  maybeHandleIncrementalResult(cacheData, incoming, query) {
94
- const { incrementalHandler } = this.queryManager;
95
- if (incrementalHandler.isIncrementalResult(incoming)) {
96
- this.incremental ||= incrementalHandler.startRequest({
107
+ if (this.incrementalHandler.isIncrementalResult(incoming)) {
108
+ this.incremental ||= this.incrementalHandler.startRequest({
97
109
  query,
98
110
  });
99
111
  return this.incremental.handle(cacheData, incoming);
100
112
  }
101
113
  return incoming;
102
114
  }
103
- markQueryResult(incoming, { document: query, variables, errorPolicy, cacheWriteBehavior, returnPartialData, fetchPolicy, networkStatus, }) {
115
+ markQueryResult(incoming, { document: query, variables, errorPolicy, cacheWriteBehavior, returnPartialData, fetchPolicy, networkStatus, prunePendingDeferFragments: prune, }) {
104
116
  const diffOptions = {
105
117
  query,
106
118
  variables,
@@ -110,154 +122,160 @@ class QueryInfo {
110
122
  // requests. To allow future notify timeouts, diff and dirty are reset as well.
111
123
  this.observableQuery?.["resetNotifications"]();
112
124
  const skipCache = cacheWriteBehavior === 0 /* CacheWriteBehavior.FORBID */;
113
- const lastDiff = skipCache ? undefined : (this.getDiff({
125
+ const diff = skipCache ? undefined : (this.getDiff({
114
126
  ...diffOptions,
115
- // Always request partial data to ensure the network incremental
127
+ // We usually request partial data to ensure the network incremental
116
128
  // result is merged with all existing data (especially true to
117
- // maintain @stream arrays with partial list items in the right order)
118
- returnPartialData: true,
119
- }));
120
- const incrementalResult = this.maybeHandleIncrementalResult(lastDiff?.result, incoming, query);
129
+ // maintain @stream arrays with partial list items in the right order
130
+ // or when chunk might otherwise replace a partial non-normalized
131
+ // object), but if we are about to throw away the result anyways due
132
+ // to the error policy (which early returns below), prune any
133
+ // pending boundaries so that CombinedGraphQLErrors contains the
134
+ // right `data` value.
135
+ returnPartialData: errorPolicy !== "none" ||
136
+ !this.incrementalHandler.extractErrors(incoming)?.length,
137
+ }, this.getIncrementalInfo({ prune })));
138
+ const incrementalResult = this.maybeHandleIncrementalResult(diff?.result, incoming, query);
121
139
  let result = {
122
140
  ...incrementalResult,
123
141
  dataState: incrementalResult.data == null ? "empty" : "complete",
124
142
  };
143
+ const hasPendingDefer = this.incremental
144
+ ?.getPendingWithInfo?.()
145
+ .some((pending) => pending.type === "defer" && !pending.delivered);
146
+ if (hasPendingDefer ||
147
+ // The Defer20220824Handler cannot track pending/completed incremental
148
+ // chunks due to its data format so we naively set dataState to
149
+ // streaming if we are still processing chunks. The only case where
150
+ // streaming is incorrect and should actually be complete is when
151
+ // both a @defer and @stream boundary is present and the @defer chunk
152
+ // has completed before the `@stream` array.
153
+ //
154
+ // Assigning the naive "streaming" value avoids a much more expensive
155
+ // pass over `result.data` that would otherwise need to traverse the
156
+ // selection sets and evaluate the data object at each defer boundary
157
+ // to see if it fulfills the selection set. For such a narrow case where
158
+ // its incorrect on a format that is now outdated is not worth the
159
+ // fix so we are ok with reporting a `streaming` here.
160
+ (!this.incremental?.getPendingWithInfo &&
161
+ this.hasNext &&
162
+ (0, internal_1.hasDirectives)(["defer"], query))) {
163
+ result.dataState = "streaming";
164
+ }
125
165
  if (skipCache) {
126
- const hasPendingDefer = this.incremental?.pending?.some((pending) => this.incremental?.getPendingType?.(pending.id) === "defer");
127
- if (hasPendingDefer ||
128
- // The Defer20220824Handler cannot track pending/completed incremental
129
- // chunks due to its data format so we naively set dataState to
130
- // streaming if we are still processing chunks. The only case where
131
- // streaming is incorrect and should actually be complete is when
132
- // both a @defer and @stream boundary is present and the @defer chunk
133
- // has completed before the `@stream` array.
134
- //
135
- // Assigning the naive "streaming" value avoids a much more expensive
136
- // pass over `result.data` that would otherwise need to traverse the
137
- // selection sets and evaluate the data object at each defer boundary
138
- // to see if it fulfills the selection set. For such a narrow case where
139
- // its incorrect on a format that is now outdated is not worth the
140
- // fix so we are ok with reporting a `streaming` here.
141
- (!this.incremental?.pending &&
142
- this.hasNext &&
143
- (0, internal_1.hasDirectives)(["defer"], query))) {
144
- result.dataState = "streaming";
145
- }
146
166
  return result;
147
167
  }
148
- if (shouldWriteResult(result, errorPolicy)) {
149
- // Using a transaction here so we have a chance to read the result
150
- // back from the cache before the watch callback fires as a result
151
- // of writeQuery, so we can store the new diff quietly and ignore
152
- // it when we receive it redundantly from the watch callback.
153
- this.cache.batch({
154
- onWatchUpdated: (
155
- // all additional options on ObservableQuery.CacheWatchOptions are
156
- // optional so we can use the type here
157
- watch, diff) => {
158
- if (watch.watcher === this.observableQuery) {
159
- // see comment on `lastOwnDiff` for explanation
160
- watch.lastOwnDiff = diff;
161
- }
162
- },
163
- update: (cache) => {
164
- const shouldWrite = this.shouldWrite(result, variables);
165
- if (shouldWrite) {
166
- cache.writeQuery({
167
- query,
168
- data: result.data,
169
- variables,
170
- overwrite: cacheWriteBehavior === 1 /* CacheWriteBehavior.OVERWRITE */,
171
- extensions: result.extensions,
172
- });
173
- this.lastWrite = {
174
- result,
175
- variables,
176
- dmCount: destructiveMethodCounts.get(this.cache),
177
- };
178
- }
179
- else {
180
- // If result is the same as the last result we received from
181
- // the network (and the variables match too), avoid writing
182
- // result into the cache again. The wisdom of skipping this
183
- // cache write is far from obvious, since any cache write
184
- // could be the one that puts the cache back into a desired
185
- // state, fixing corruption or missing data. However, if we
186
- // always write every network result into the cache, we enable
187
- // feuds between queries competing to update the same data in
188
- // incompatible ways, which can lead to an endless cycle of
189
- // cache broadcasts and useless network requests. As with any
190
- // feud, eventually one side must step back from the brink,
191
- // letting the other side(s) have the last word(s). There may
192
- // be other points where we could break this cycle, such as
193
- // silencing the broadcast for cache.writeQuery (not a good
194
- // idea, since it just delays the feud a bit) or somehow
195
- // avoiding the network request that just happened (also bad,
196
- // because the server could return useful new data). All
197
- // options considered, skipping this cache write seems to be
198
- // the least damaging place to break the cycle, because it
199
- // reflects the intuition that we recently wrote this exact
200
- // result into the cache, so the cache *should* already/still
201
- // contain this data. If some other query has clobbered that
202
- // data in the meantime, that's too bad, but there will be no
203
- // winners if every query blindly reverts to its own version
204
- // of the data. This approach also gives the network a chance
205
- // to return new data, which will be written into the cache as
206
- // usual, notifying only those queries that are directly
207
- // affected by the cache updates, as usual. In the future, an
208
- // even more sophisticated cache could perhaps prevent or
209
- // mitigate the clobbering somehow, but that would make this
210
- // particular cache write even less important, and thus
211
- // skipping it would be even safer than it is today.
212
- if (lastDiff && lastDiff.complete) {
213
- // Reuse data from the last good (complete) diff that we
214
- // received, when possible.
215
- result = {
216
- ...result,
217
- data: lastDiff.result,
218
- dataState: "complete",
219
- };
220
- return;
221
- }
222
- // If the previous this.diff was incomplete, fall through to
223
- // re-reading the latest data with cache.diff, below.
224
- }
225
- const isNetworkOnly = fetchPolicy === "network-only" &&
226
- networkStatus !== networkStatus_js_1.NetworkStatus.refetch;
227
- const { dataState, result: diffResult } = this.getDiff({
228
- ...diffOptions,
229
- // Never deliver partial data for network-only requests
230
- returnPartialData: returnPartialData && !isNetworkOnly,
231
- }, this.getIncrementalInfo(result, { isNetworkOnly }));
232
- if (dataState === "complete" ||
233
- (returnPartialData && dataState === "partial" && shouldWrite) ||
234
- (this.hasNext && dataState === "streaming")) {
235
- result = { ...result, data: diffResult, dataState };
236
- }
237
- },
238
- });
239
- }
240
- else {
168
+ if (!shouldWriteResult(result, errorPolicy)) {
241
169
  this.lastWrite = void 0;
170
+ return result;
242
171
  }
172
+ let written = false;
173
+ // Using a transaction here so we have a chance to read the result
174
+ // back from the cache before the watch callback fires as a result
175
+ // of writeQuery, so we can store the new diff quietly and ignore
176
+ // it when we receive it redundantly from the watch callback.
177
+ this.cache.batch({
178
+ onWatchUpdated: (
179
+ // all additional options on ObservableQuery.CacheWatchOptions are
180
+ // optional so we can use the type here
181
+ watch, diff) => {
182
+ if (watch.watcher === this.observableQuery) {
183
+ // see comment on `lastOwnDiff` for explanation
184
+ watch.lastOwnDiff = diff;
185
+ }
186
+ },
187
+ update: (cache) => {
188
+ const shouldWrite = this.shouldWrite(result, variables);
189
+ // If result is the same as the last result we received from
190
+ // the network (and the variables match too), avoid writing
191
+ // result into the cache again. The wisdom of skipping this
192
+ // cache write is far from obvious, since any cache write
193
+ // could be the one that puts the cache back into a desired
194
+ // state, fixing corruption or missing data. However, if we
195
+ // always write every network result into the cache, we enable
196
+ // feuds between queries competing to update the same data in
197
+ // incompatible ways, which can lead to an endless cycle of
198
+ // cache broadcasts and useless network requests. As with any
199
+ // feud, eventually one side must step back from the brink,
200
+ // letting the other side(s) have the last word(s). There may
201
+ // be other points where we could break this cycle, such as
202
+ // silencing the broadcast for cache.writeQuery (not a good
203
+ // idea, since it just delays the feud a bit) or somehow
204
+ // avoiding the network request that just happened (also bad,
205
+ // because the server could return useful new data). All
206
+ // options considered, skipping this cache write seems to be
207
+ // the least damaging place to break the cycle, because it
208
+ // reflects the intuition that we recently wrote this exact
209
+ // result into the cache, so the cache *should* already/still
210
+ // contain this data. If some other query has clobbered that
211
+ // data in the meantime, that's too bad, but there will be no
212
+ // winners if every query blindly reverts to its own version
213
+ // of the data. This approach also gives the network a chance
214
+ // to return new data, which will be written into the cache as
215
+ // usual, notifying only those queries that are directly
216
+ // affected by the cache updates, as usual. In the future, an
217
+ // even more sophisticated cache could perhaps prevent or
218
+ // mitigate the clobbering somehow, but that would make this
219
+ // particular cache write even less important, and thus
220
+ // skipping it would be even safer than it is today.
221
+ if (shouldWrite) {
222
+ cache.writeQuery({
223
+ query,
224
+ data: result.data,
225
+ variables,
226
+ overwrite: cacheWriteBehavior === 1 /* CacheWriteBehavior.OVERWRITE */,
227
+ extensions: result.extensions,
228
+ });
229
+ this.lastWrite = {
230
+ result,
231
+ variables,
232
+ dmCount: destructiveMethodCounts.get(this.cache),
233
+ hasNext: this.hasNext,
234
+ };
235
+ written = true;
236
+ }
237
+ const { dataState, result: diffResult } = this.getDiff({
238
+ ...diffOptions,
239
+ returnPartialData: returnPartialData &&
240
+ // Never deliver partial data for network-only requests
241
+ (fetchPolicy !== "network-only" ||
242
+ networkStatus === networkStatus_js_1.NetworkStatus.refetch),
243
+ }, this.getIncrementalInfo({ prune }));
244
+ if (dataState === "complete" ||
245
+ dataState === "streaming" ||
246
+ (returnPartialData && dataState === "partial" && shouldWrite)) {
247
+ result = { ...result, data: diffResult, dataState };
248
+ }
249
+ else if (environment_1.__DEV__ &&
250
+ written &&
251
+ // A result that is still streaming is expected to read back
252
+ // incomplete until the remaining chunks arrive.
253
+ !this.hasNext) {
254
+ warnAboutPartialCacheResult(query, result.data,
255
+ // Always show the partial result for debugging, otherwise the user
256
+ // sees `null` when `returnPartialData` is false which isn't helpful
257
+ // for figuring out where the problem is.
258
+ cache.diff({ ...diffOptions, returnPartialData: true }));
259
+ }
260
+ },
261
+ });
243
262
  return result;
244
263
  }
245
- getIncrementalInfo(result, { isNetworkOnly }) {
246
- const pending = this.incremental?.pending ?? [];
247
- const streamInfo = result.extensions?.[internal_1.streamInfoSymbol]?.deref();
264
+ getIncrementalInfo({ prune }) {
265
+ const pending = this.incremental?.getPendingWithInfo?.() ?? [];
266
+ const streamInfo = this.incremental?.streamInfo;
248
267
  const incrementalInfo = { streamInfo };
249
268
  // We don't want to deliver stream items or complete defer boundaries
250
269
  // for a network-only request if they haven't yet streamed from the
251
270
  // network. We record all the still-pending paths so that cache.diff
252
271
  // can prune complete defer/stream boundaries at those paths.
253
- if (isNetworkOnly) {
272
+ if (prune) {
254
273
  for (const item of pending) {
255
- const type = this.incremental?.getPendingType?.(item.id);
256
- if (type === "defer") {
274
+ if (item.type === "defer" && !item.delivered) {
257
275
  incrementalInfo.deferInfo ||= new trie_1.Trie(true, () => true);
258
- incrementalInfo.deferInfo.lookupArray(item.path);
276
+ incrementalInfo.deferInfo.lookupArray(item.path.concat(item.label || []));
259
277
  }
260
- else if (streamInfo && type === "stream") {
278
+ else if (streamInfo && item.type === "stream") {
261
279
  streamInfo.lookupArray(item.path).state.truncate = true;
262
280
  }
263
281
  }
@@ -265,17 +283,13 @@ class QueryInfo {
265
283
  return incrementalInfo;
266
284
  }
267
285
  getDiff(options, incrementalInfo) {
268
- if (this.cache[internal_1.handleIncrementalSymbol]) {
269
- return this.cache.diff({
270
- ...options,
271
- [internal_1.handleIncrementalSymbol]: incrementalInfo || true,
272
- });
286
+ const diff = this.cache.diff({
287
+ ...options,
288
+ [internal_1.handleIncrementalSymbol]: incrementalInfo,
289
+ });
290
+ if ("dataState" in diff) {
291
+ return diff;
273
292
  }
274
- // returnPartialData is overridden for backwards compatibility with caches
275
- // that don't handle incremental results. Without this, in-flight
276
- // incremental cache data would come back null when returnPartialData is
277
- // false due to the partial result.
278
- const diff = this.cache.diff({ ...options, returnPartialData: true });
279
293
  return {
280
294
  ...diff,
281
295
  dataState: diff.complete ? "complete"
@@ -478,6 +492,15 @@ class QueryInfo {
478
492
  }
479
493
  }
480
494
  exports.QueryInfo = QueryInfo;
495
+ function warnAboutPartialCacheResult(query, networkResult, diff) {
496
+ __DEV__ && invariant_1.invariant.warn(
497
+ 89,
498
+ (0, internal_1.getOperationName)(query, "(anonymous)"),
499
+ diff.missing?.missing,
500
+ networkResult,
501
+ diff.result
502
+ );
503
+ }
481
504
  function shouldWriteResult(result, errorPolicy = "none") {
482
505
  const ignoreErrors = errorPolicy === "ignore" || errorPolicy === "all";
483
506
  let writeWithErrors = !(0, internal_1.graphQLResultHasError)(result);