@apollo/client 4.3.0-alpha.3 → 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 (220) hide show
  1. package/CHANGELOG.md +120 -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/core/types/Cache.d.cts +25 -0
  7. package/__cjs/cache/index.cjs.map +1 -1
  8. package/__cjs/cache/index.d.cts +1 -1
  9. package/__cjs/cache/inmemory/entityStore.cjs +3 -3
  10. package/__cjs/cache/inmemory/inMemoryCache.cjs.map +1 -1
  11. package/__cjs/cache/inmemory/inMemoryCache.d.cts +6 -3
  12. package/__cjs/cache/inmemory/key-extractor.cjs +1 -1
  13. package/__cjs/cache/inmemory/policies.cjs +4 -4
  14. package/__cjs/cache/inmemory/readFromStore.cjs +477 -49
  15. package/__cjs/cache/inmemory/readFromStore.cjs.map +1 -1
  16. package/__cjs/cache/inmemory/readFromStore.d.cts +11 -2
  17. package/__cjs/cache/inmemory/types.d.cts +10 -1
  18. package/__cjs/cache/inmemory/writeToStore.cjs +5 -9
  19. package/__cjs/cache/inmemory/writeToStore.cjs.map +1 -1
  20. package/__cjs/core/ApolloClient.cjs +21 -22
  21. package/__cjs/core/ApolloClient.cjs.map +1 -1
  22. package/__cjs/core/ApolloClient.d.cts +44 -10
  23. package/__cjs/core/ObservableQuery.cjs +46 -5
  24. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  25. package/__cjs/core/ObservableQuery.d.cts +12 -1
  26. package/__cjs/core/QueryInfo.cjs +206 -269
  27. package/__cjs/core/QueryInfo.cjs.map +1 -1
  28. package/__cjs/core/QueryInfo.d.cts +20 -4
  29. package/__cjs/core/QueryManager.cjs +52 -28
  30. package/__cjs/core/QueryManager.cjs.map +1 -1
  31. package/__cjs/core/RefetchEventManager.cjs +3 -3
  32. package/__cjs/core/dataStateErrorCache.cjs +12 -0
  33. package/__cjs/core/dataStateErrorCache.cjs.map +1 -0
  34. package/__cjs/core/dataStateErrorCache.d.cts +3 -0
  35. package/__cjs/core/types.d.cts +2 -0
  36. package/__cjs/incremental/handlers/defer20220824.cjs.map +1 -1
  37. package/__cjs/incremental/handlers/defer20220824.d.cts +2 -4
  38. package/__cjs/incremental/handlers/graphql17Alpha9.cjs +46 -15
  39. package/__cjs/incremental/handlers/graphql17Alpha9.cjs.map +1 -1
  40. package/__cjs/incremental/handlers/graphql17Alpha9.d.cts +29 -6
  41. package/__cjs/incremental/types.d.cts +55 -4
  42. package/__cjs/invariantErrorCodes.cjs +50 -31
  43. package/__cjs/react/hooks/useBackgroundQuery.cjs.map +1 -1
  44. package/__cjs/react/hooks/useBackgroundQuery.d.cts +100 -47
  45. package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
  46. package/__cjs/react/hooks/useLazyQuery.d.cts +26 -20
  47. package/__cjs/react/hooks/useLoadableQuery.cjs.map +1 -1
  48. package/__cjs/react/hooks/useLoadableQuery.d.cts +23 -15
  49. package/__cjs/react/hooks/useMutation.cjs.map +1 -1
  50. package/__cjs/react/hooks/useMutation.d.cts +10 -7
  51. package/__cjs/react/hooks/useQuery.cjs.map +1 -1
  52. package/__cjs/react/hooks/useQuery.d.cts +74 -32
  53. package/__cjs/react/hooks/useQueryRefHandlers.cjs.map +1 -1
  54. package/__cjs/react/hooks/useSuspenseQuery.cjs.map +1 -1
  55. package/__cjs/react/hooks/useSuspenseQuery.d.cts +83 -34
  56. package/__cjs/react/internal/cache/QueryReference.cjs.map +1 -1
  57. package/__cjs/react/internal/types.d.cts +14 -3
  58. package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
  59. package/__cjs/react/query-preloader/createQueryPreloader.d.cts +20 -5
  60. package/__cjs/testing/core/mocking/mockLink.cjs +21 -1
  61. package/__cjs/testing/core/mocking/mockLink.cjs.map +1 -1
  62. package/__cjs/testing/core/mocking/mockLink.d.cts +1 -2
  63. package/__cjs/utilities/caching/sizes.cjs.map +1 -1
  64. package/__cjs/utilities/caching/sizes.d.cts +39 -1
  65. package/__cjs/utilities/internal/StreamArrayState.cjs +27 -0
  66. package/__cjs/utilities/internal/StreamArrayState.cjs.map +1 -0
  67. package/__cjs/utilities/internal/StreamArrayState.d.cts +12 -0
  68. package/__cjs/utilities/internal/addDeferFragmentLabels.cjs +33 -0
  69. package/__cjs/utilities/internal/addDeferFragmentLabels.cjs.map +1 -0
  70. package/__cjs/utilities/internal/addDeferFragmentLabels.d.cts +3 -0
  71. package/__cjs/utilities/internal/constants.cjs +20 -1
  72. package/__cjs/utilities/internal/constants.cjs.map +1 -1
  73. package/__cjs/utilities/internal/constants.d.cts +19 -0
  74. package/__cjs/utilities/internal/getDirectiveArgValue.cjs +19 -0
  75. package/__cjs/utilities/internal/getDirectiveArgValue.cjs.map +1 -0
  76. package/__cjs/utilities/internal/getDirectiveArgValue.d.cts +9 -0
  77. package/__cjs/utilities/internal/getFragmentFromSelection.cjs +1 -1
  78. package/__cjs/utilities/internal/getFragmentFromSelection.cjs.map +1 -1
  79. package/__cjs/utilities/internal/getMemoryInternals.cjs +6 -0
  80. package/__cjs/utilities/internal/getMemoryInternals.cjs.map +1 -1
  81. package/__cjs/utilities/internal/getMemoryInternals.d.cts +4 -0
  82. package/__cjs/utilities/internal/index.cjs +13 -4
  83. package/__cjs/utilities/internal/index.cjs.map +1 -1
  84. package/__cjs/utilities/internal/index.d.cts +7 -3
  85. package/__cjs/utilities/internal/isDeferredFragment.cjs +19 -4
  86. package/__cjs/utilities/internal/isDeferredFragment.cjs.map +1 -1
  87. package/__cjs/utilities/internal/isDeferredFragment.d.cts +6 -1
  88. package/__cjs/utilities/internal/isStreamField.cjs +37 -0
  89. package/__cjs/utilities/internal/isStreamField.cjs.map +1 -0
  90. package/__cjs/utilities/internal/isStreamField.d.cts +9 -0
  91. package/__cjs/utilities/internal/makeStreamInfoTrie.cjs +17 -0
  92. package/__cjs/utilities/internal/makeStreamInfoTrie.cjs.map +1 -0
  93. package/__cjs/utilities/internal/makeStreamInfoTrie.d.cts +8 -0
  94. package/__cjs/utilities/internal/types/DeferInfo.cjs +3 -0
  95. package/__cjs/utilities/internal/types/DeferInfo.cjs.map +1 -0
  96. package/__cjs/utilities/internal/types/DeferInfo.d.cts +10 -0
  97. package/__cjs/utilities/internal/types/StreamInfoTrie.d.cts +2 -0
  98. package/__cjs/version.cjs +1 -1
  99. package/cache/core/cache.d.ts +5 -1
  100. package/cache/core/cache.js +1 -1
  101. package/cache/core/cache.js.map +1 -1
  102. package/cache/core/types/Cache.d.ts +25 -0
  103. package/cache/core/types/Cache.js.map +1 -1
  104. package/cache/index.d.ts +1 -1
  105. package/cache/index.js.map +1 -1
  106. package/cache/inmemory/entityStore.js +3 -3
  107. package/cache/inmemory/inMemoryCache.d.ts +6 -3
  108. package/cache/inmemory/inMemoryCache.js.map +1 -1
  109. package/cache/inmemory/key-extractor.js +1 -1
  110. package/cache/inmemory/policies.js +4 -4
  111. package/cache/inmemory/readFromStore.d.ts +11 -2
  112. package/cache/inmemory/readFromStore.js +478 -50
  113. package/cache/inmemory/readFromStore.js.map +1 -1
  114. package/cache/inmemory/types.d.ts +11 -2
  115. package/cache/inmemory/types.js.map +1 -1
  116. package/cache/inmemory/writeToStore.js +6 -10
  117. package/cache/inmemory/writeToStore.js.map +1 -1
  118. package/core/ApolloClient.d.ts +44 -10
  119. package/core/ApolloClient.js +21 -22
  120. package/core/ApolloClient.js.map +1 -1
  121. package/core/ObservableQuery.d.ts +12 -1
  122. package/core/ObservableQuery.js +46 -5
  123. package/core/ObservableQuery.js.map +1 -1
  124. package/core/QueryInfo.d.ts +20 -4
  125. package/core/QueryInfo.js +202 -269
  126. package/core/QueryInfo.js.map +1 -1
  127. package/core/QueryManager.js +52 -28
  128. package/core/QueryManager.js.map +1 -1
  129. package/core/RefetchEventManager.js +3 -3
  130. package/core/dataStateErrorCache.d.ts +3 -0
  131. package/core/dataStateErrorCache.js +9 -0
  132. package/core/dataStateErrorCache.js.map +1 -0
  133. package/core/types.d.ts +2 -0
  134. package/core/types.js.map +1 -1
  135. package/incremental/handlers/defer20220824.d.ts +2 -4
  136. package/incremental/handlers/defer20220824.js.map +1 -1
  137. package/incremental/handlers/graphql17Alpha9.d.ts +29 -6
  138. package/incremental/handlers/graphql17Alpha9.js +47 -16
  139. package/incremental/handlers/graphql17Alpha9.js.map +1 -1
  140. package/incremental/types.d.ts +55 -4
  141. package/incremental/types.js.map +1 -1
  142. package/invariantErrorCodes.js +50 -31
  143. package/package.json +1 -1
  144. package/react/hooks/useBackgroundQuery.d.ts +100 -47
  145. package/react/hooks/useBackgroundQuery.js.map +1 -1
  146. package/react/hooks/useLazyQuery.d.ts +26 -20
  147. package/react/hooks/useLazyQuery.js.map +1 -1
  148. package/react/hooks/useLoadableQuery.d.ts +23 -15
  149. package/react/hooks/useLoadableQuery.js.map +1 -1
  150. package/react/hooks/useMutation.d.ts +10 -7
  151. package/react/hooks/useMutation.js.map +1 -1
  152. package/react/hooks/useQuery.d.ts +74 -32
  153. package/react/hooks/useQuery.js.map +1 -1
  154. package/react/hooks/useQueryRefHandlers.js.map +1 -1
  155. package/react/hooks/useSuspenseQuery.d.ts +83 -34
  156. package/react/hooks/useSuspenseQuery.js.map +1 -1
  157. package/react/hooks-compiled/useBackgroundQuery.d.ts +100 -47
  158. package/react/hooks-compiled/useBackgroundQuery.js.map +1 -1
  159. package/react/hooks-compiled/useLazyQuery.d.ts +26 -20
  160. package/react/hooks-compiled/useLazyQuery.js.map +1 -1
  161. package/react/hooks-compiled/useLoadableQuery.d.ts +23 -15
  162. package/react/hooks-compiled/useLoadableQuery.js.map +1 -1
  163. package/react/hooks-compiled/useMutation.d.ts +10 -7
  164. package/react/hooks-compiled/useMutation.js.map +1 -1
  165. package/react/hooks-compiled/useQuery.d.ts +74 -32
  166. package/react/hooks-compiled/useQuery.js.map +1 -1
  167. package/react/hooks-compiled/useQueryRefHandlers.js.map +1 -1
  168. package/react/hooks-compiled/useSuspenseQuery.d.ts +83 -34
  169. package/react/hooks-compiled/useSuspenseQuery.js.map +1 -1
  170. package/react/internal/cache/QueryReference.js.map +1 -1
  171. package/react/internal/types.d.ts +15 -4
  172. package/react/internal/types.js.map +1 -1
  173. package/react/query-preloader/createQueryPreloader.d.ts +20 -5
  174. package/react/query-preloader/createQueryPreloader.js.map +1 -1
  175. package/testing/core/mocking/mockLink.d.ts +1 -2
  176. package/testing/core/mocking/mockLink.js +21 -1
  177. package/testing/core/mocking/mockLink.js.map +1 -1
  178. package/utilities/caching/sizes.d.ts +39 -1
  179. package/utilities/caching/sizes.js.map +1 -1
  180. package/utilities/internal/StreamArrayState.d.ts +12 -0
  181. package/utilities/internal/StreamArrayState.js +23 -0
  182. package/utilities/internal/StreamArrayState.js.map +1 -0
  183. package/utilities/internal/addDeferFragmentLabels.d.ts +3 -0
  184. package/utilities/internal/addDeferFragmentLabels.js +30 -0
  185. package/utilities/internal/addDeferFragmentLabels.js.map +1 -0
  186. package/utilities/internal/constants.d.ts +19 -0
  187. package/utilities/internal/constants.js +19 -0
  188. package/utilities/internal/constants.js.map +1 -1
  189. package/utilities/internal/getDirectiveArgValue.d.ts +9 -0
  190. package/utilities/internal/getDirectiveArgValue.js +16 -0
  191. package/utilities/internal/getDirectiveArgValue.js.map +1 -0
  192. package/utilities/internal/getFragmentFromSelection.js +1 -1
  193. package/utilities/internal/getFragmentFromSelection.js.map +1 -1
  194. package/utilities/internal/getMemoryInternals.d.ts +4 -0
  195. package/utilities/internal/getMemoryInternals.js +6 -0
  196. package/utilities/internal/getMemoryInternals.js.map +1 -1
  197. package/utilities/internal/index.d.ts +7 -3
  198. package/utilities/internal/index.js +6 -2
  199. package/utilities/internal/index.js.map +1 -1
  200. package/utilities/internal/isDeferredFragment.d.ts +7 -2
  201. package/utilities/internal/isDeferredFragment.js +19 -4
  202. package/utilities/internal/isDeferredFragment.js.map +1 -1
  203. package/utilities/internal/isStreamField.d.ts +9 -0
  204. package/utilities/internal/isStreamField.js +34 -0
  205. package/utilities/internal/isStreamField.js.map +1 -0
  206. package/utilities/internal/makeStreamInfoTrie.d.ts +8 -0
  207. package/utilities/internal/makeStreamInfoTrie.js +14 -0
  208. package/utilities/internal/makeStreamInfoTrie.js.map +1 -0
  209. package/utilities/internal/types/DeferInfo.d.ts +10 -0
  210. package/utilities/internal/types/DeferInfo.js +2 -0
  211. package/utilities/internal/types/DeferInfo.js.map +1 -0
  212. package/utilities/internal/types/StreamInfoTrie.d.ts +2 -0
  213. package/utilities/internal/types/StreamInfoTrie.js.map +1 -1
  214. package/version.js +1 -1
  215. package/__cjs/utilities/internal/collectSiblingFields.cjs +0 -61
  216. package/__cjs/utilities/internal/collectSiblingFields.cjs.map +0 -1
  217. package/__cjs/utilities/internal/collectSiblingFields.d.cts +0 -21
  218. package/utilities/internal/collectSiblingFields.d.ts +0 -21
  219. package/utilities/internal/collectSiblingFields.js +0 -58
  220. package/utilities/internal/collectSiblingFields.js.map +0 -1
@@ -5,6 +5,8 @@ const {
5
5
 
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.StoreReader = void 0;
8
+ const equality_1 = require("@wry/equality");
9
+ const trie_1 = require("@wry/trie");
8
10
  const graphql_1 = require("graphql");
9
11
  const optimism_1 = require("optimism");
10
12
  const utilities_1 = require("@apollo/client/utilities");
@@ -22,8 +24,11 @@ class StoreReader {
22
24
  executeSelectionSet;
23
25
  // cached version of executeSubSelectedArray
24
26
  executeSubSelectedArray;
27
+ prunePartialStreamArray;
28
+ prunePartialBoundaries;
25
29
  config;
26
30
  knownResults = new WeakMap();
31
+ keyMaker = new trie_1.Trie();
27
32
  constructor(config) {
28
33
  this.config = config;
29
34
  // memoized functions in this class will be "garbage-collected"
@@ -66,47 +71,124 @@ class StoreReader {
66
71
  }
67
72
  },
68
73
  });
74
+ this.prunePartialBoundaries = (0, optimism_1.wrap)((options) => this.prunePartialBoundariesImpl(options), {
75
+ max: utilities_1.cacheSizes["inMemoryCache.prunePartialBoundaries"] ||
76
+ 20000 /* defaultCacheSizes["inMemoryCache.prunePartialBoundaries"] */,
77
+ makeCacheKey: ({ boundaries, context, selectionSet }) => {
78
+ if ((0, entityStore_js_1.supportsResultCaching)(context.store)) {
79
+ return this.keyMaker.lookup(selectionSet, boundaries, context.streamInfo, context.deferInfo);
80
+ }
81
+ },
82
+ });
83
+ this.prunePartialStreamArray = (0, optimism_1.wrap)((options) => {
84
+ const { field, context, path } = options;
85
+ if ((0, internal_1.isStreamField)(field, context.variables)) {
86
+ context.streamInfo?.lookupArray(path).state.depend();
87
+ }
88
+ return this.prunePartialStreamArrayImpl(options);
89
+ }, {
90
+ max: utilities_1.cacheSizes["inMemoryCache.prunePartialStreamArray"] ||
91
+ 20000 /* defaultCacheSizes["inMemoryCache.prunePartialStreamArray"] */,
92
+ makeCacheKey: ({ field, context, boundaries }) => {
93
+ if ((0, entityStore_js_1.supportsResultCaching)(context.store)) {
94
+ return this.keyMaker.lookup(field, boundaries, context.streamInfo, context.deferInfo);
95
+ }
96
+ },
97
+ });
69
98
  }
70
- /**
71
- * Given a store and a query, return as much of the result as possible and
72
- * identify if any data was missing from the store.
73
- */
74
- diffQueryAgainstStore({ store, query, rootId = "ROOT_QUERY", variables, returnPartialData = true, }) {
99
+ diffQueryAgainstStore({ store, query, rootId = "ROOT_QUERY", variables, returnPartialData = true, ...options }) {
100
+ const returnIncremental = Object.hasOwn(options, internal_1.handleIncrementalSymbol);
75
101
  const policies = this.config.cache.policies;
76
- variables = {
77
- ...(0, internal_1.getDefaultValues)((0, internal_1.getQueryDefinition)(query)),
78
- ...variables,
79
- };
102
+ variables = (0, internal_1.compact)((0, internal_1.getDefaultValues)((0, internal_1.getQueryDefinition)(query)), variables);
80
103
  const rootRef = (0, internal_1.makeReference)(rootId);
81
- const execResult = this.executeSelectionSet({
104
+ const context = {
105
+ store,
106
+ query,
107
+ policies,
108
+ variables,
109
+ varString: (0, utilities_1.canonicalStringify)(variables),
110
+ ...(0, helpers_js_1.extractFragmentContext)(query, this.config.fragments),
111
+ ...options[internal_1.handleIncrementalSymbol],
112
+ };
113
+ let execResult = this.executeSelectionSet({
82
114
  selectionSet: (0, internal_1.getMainDefinition)(query).selectionSet,
83
115
  objectOrReference: rootRef,
84
116
  enclosingRef: rootRef,
85
- context: {
86
- store,
87
- query,
88
- policies,
89
- variables,
90
- varString: (0, utilities_1.canonicalStringify)(variables),
91
- ...(0, helpers_js_1.extractFragmentContext)(query, this.config.fragments),
92
- },
117
+ context,
93
118
  });
94
- let missing;
95
- if (execResult.missing) {
96
- missing = new common_js_1.MissingFieldError(firstMissing(execResult.missing), execResult.missing, query, variables);
119
+ // Since executeSelectionSet doesn't know about returnPartialData, we need
120
+ // to perform a 2nd pass over the result to prune any fields inside
121
+ // partial defer boundaries. The "deferPartial" data state tells us that the
122
+ // only part of the result that contributed to its partiality is data inside
123
+ // a defer boundary.
124
+ if (returnIncremental && shouldPrune(execResult, context)) {
125
+ const pruned = this.prunePartialBoundaries({
126
+ selectionSet: (0, internal_1.getMainDefinition)(query).selectionSet,
127
+ data: execResult.result,
128
+ boundaries: execResult.partialBoundaries,
129
+ context,
130
+ path: [],
131
+ });
132
+ const changed = execResult.result !== pruned.result;
133
+ // It's possible that pruning didn't actually change the result which can
134
+ // happen if a defer boundary is misclassified as "deferPartial" instead
135
+ // of "streaming" (sibling defer boundaries with overlapping selection
136
+ // sets, one of which is complete). In this case, pruning corrects the
137
+ // dataState to streaming instead of leaving it as partial. If we tolerate
138
+ // partial results and pruning changed the result by dropping fields, then
139
+ // we want to keep the original execResult which contains the partial
140
+ // data.
141
+ if (!changed || !returnPartialData) {
142
+ // Omit `missing` property since pruning puts it in a state that doesn't
143
+ // report missing fields.
144
+ execResult = {
145
+ result: pruned.result,
146
+ partialBoundaries: execResult.partialBoundaries,
147
+ dataState: pruned.dataState,
148
+ };
149
+ }
97
150
  }
98
- const complete = !missing;
99
- const { result } = execResult;
100
- return {
101
- result: complete ? result
102
- : returnPartialData ?
103
- Object.keys(result).length === 0 ?
104
- null
105
- : result
106
- : null,
151
+ let { result, dataState, missing } = execResult;
152
+ // Evaluate this condition before we start mucking with dataState for the
153
+ // publicly returned value
154
+ const includeMissing = !!missing &&
155
+ // We don't need to report missing fields inside defer boundaries since
156
+ // the "streaming" dataState tells us that the only missing fields in
157
+ // the object is inside a defer boundary.
158
+ (dataState !== "streaming" || !returnIncremental);
159
+ // If we get all root @defer boundaries with an empty result, report it as
160
+ // empty instead of streaming.
161
+ if (dataState === "streaming" && Object.keys(result).length === 0) {
162
+ dataState = "empty";
163
+ }
164
+ let missingError;
165
+ if (dataState === "deferPartial" ||
166
+ dataState === "streamPartial" ||
167
+ (dataState === "streaming" && !returnIncremental)) {
168
+ dataState = "partial";
169
+ }
170
+ if (dataState === "partial" && !returnPartialData) {
171
+ dataState = "empty";
172
+ }
173
+ const complete = dataState === "complete";
174
+ const keepResult = complete ||
175
+ dataState === "streaming" ||
176
+ (returnPartialData && Object.keys(result).length);
177
+ const diffResult = {
178
+ result: keepResult ? result : null,
107
179
  complete,
108
- missing,
180
+ get missing() {
181
+ if (includeMissing) {
182
+ missingError ||= new common_js_1.MissingFieldError(firstMissing(missing), missing, query, variables);
183
+ }
184
+ return missingError;
185
+ },
109
186
  };
187
+ if (returnIncremental) {
188
+ diffResult.dataState =
189
+ dataState;
190
+ }
191
+ return diffResult;
110
192
  }
111
193
  isFresh(result, parent, selectionSet, context) {
112
194
  if ((0, entityStore_js_1.supportsResultCaching)(context.store) &&
@@ -125,14 +207,18 @@ class StoreReader {
125
207
  !context.store.has(objectOrReference.__ref)) {
126
208
  return {
127
209
  result: {},
210
+ dataState: "empty",
128
211
  missing: `Dangling reference to missing ${objectOrReference.__ref} object`,
212
+ partialBoundaries: new PartialBoundaries(),
129
213
  };
130
214
  }
131
215
  const { variables, policies, store } = context;
132
216
  const typename = store.getFieldValue(objectOrReference, "__typename");
133
217
  const objectsToMerge = [];
218
+ let dataState;
134
219
  let missing;
135
220
  const missingMerger = new internal_1.DeepMerger();
221
+ const partialBoundaries = new PartialBoundaries();
136
222
  if (typeof typename === "string" && !policies.rootIdsByTypename[typename]) {
137
223
  // Ensure we always include a default value for the __typename
138
224
  // field, if we have one. Note that this field can be overridden by other
@@ -145,7 +231,7 @@ class StoreReader {
145
231
  [resultName]: result.missing,
146
232
  });
147
233
  }
148
- return result.result;
234
+ return result;
149
235
  }
150
236
  const workSet = new Set(selectionSet.selections);
151
237
  workSet.forEach((selection) => {
@@ -163,25 +249,39 @@ class StoreReader {
163
249
  const resultName = (0, internal_1.resultKeyNameFromField)(selection);
164
250
  if (fieldValue === void 0) {
165
251
  if (!utilities_1.addTypenameToDocument.added(selection)) {
252
+ const id = (0, utilities_1.isReference)(objectOrReference) ? objectOrReference.__ref
253
+ : objectOrReference ? policies.identify(objectOrReference)[0]
254
+ : undefined;
166
255
  missing = missingMerger.merge(missing, {
167
- [resultName]: `Can't find field '${selection.name.value}' on ${(0, utilities_1.isReference)(objectOrReference) ?
168
- objectOrReference.__ref + " object"
169
- : "object " + JSON.stringify(objectOrReference, null, 2)}`,
256
+ [resultName]: `Can't find field '${selection.name.value}' on ${id ?
257
+ `${id} object`
258
+ : `object ${JSON.stringify(objectOrReference || {}, null, 2)}`}`,
170
259
  });
260
+ dataState = mergeDataState(dataState, "empty");
171
261
  }
172
262
  }
173
263
  else if ((0, internal_1.isArray)(fieldValue)) {
174
264
  if (fieldValue.length > 0) {
175
- fieldValue = handleMissing(this.executeSubSelectedArray({
265
+ const execResult = handleMissing(this.executeSubSelectedArray({
176
266
  field: selection,
177
267
  array: fieldValue,
178
268
  enclosingRef,
179
269
  context,
180
270
  }), resultName);
271
+ fieldValue = execResult.result;
272
+ dataState = mergeDataState(dataState, execResult.dataState);
273
+ partialBoundaries.set(resultName, execResult.partialBoundaries);
274
+ }
275
+ else {
276
+ dataState = mergeDataState(dataState, "complete");
181
277
  }
182
278
  }
183
279
  else if (!selection.selectionSet) {
184
- // do nothing
280
+ // Auto-inserted __typename should not affect dataState (see empty
281
+ // @defer boundaries, which must stay "empty" → parent "streaming").
282
+ if (!utilities_1.addTypenameToDocument.added(selection)) {
283
+ dataState = mergeDataState(dataState, "complete");
284
+ }
185
285
  }
186
286
  else if (fieldValue != null) {
187
287
  if (environment_1.__DEV__) {
@@ -189,19 +289,35 @@ class StoreReader {
189
289
  if (typename) {
190
290
  const policy = policies["getFieldPolicy"](typename, fieldName);
191
291
  if (policy?.scalar) {
192
- __DEV__ && invariant_1.invariant.warn(112, `${typename}.${fieldName}`, policy.scalar);
292
+ __DEV__ && invariant_1.invariant.warn(113, `${typename}.${fieldName}`, policy.scalar);
193
293
  }
194
294
  }
195
295
  }
196
296
  // In this case, because we know the field has a selection set,
197
297
  // it must be trying to query a GraphQLObjectType, which is why
198
298
  // fieldValue must be != null.
199
- fieldValue = handleMissing(this.executeSelectionSet({
299
+ const execResult = handleMissing(this.executeSelectionSet({
200
300
  selectionSet: selection.selectionSet,
201
301
  objectOrReference: fieldValue,
202
302
  enclosingRef: (0, utilities_1.isReference)(fieldValue) ? fieldValue : enclosingRef,
203
303
  context,
204
304
  }), resultName);
305
+ fieldValue = execResult.result;
306
+ partialBoundaries.set(resultName, execResult.partialBoundaries);
307
+ // If the object's fields resolved to an "empty" dataState (e.g. no
308
+ // field resolved with a non-undefined value), but the fieldValue
309
+ // object itself is present, this object should be considered
310
+ // partial instead of empty. This also ensures defer boundaries that
311
+ // select this object remain as partial defer boundaries rather than
312
+ // mistakenly get reported as streaming. This is especially necessary
313
+ // when combined with GraphQL Codegen which generates its type and
314
+ // relies on the outer object to be absent when its fields haven't
315
+ // streamed in. Reporting the defer boundary as "streaming" instead of
316
+ // "partial" would otherwise have the potential to cause runtime
317
+ // crashes since the runtime values and types would not line up
318
+ // properly (types expect object to be undefined, but its instead
319
+ // present without its fields)
320
+ dataState = mergeDataState(dataState, execResult.dataState === "empty" ? "partial" : execResult.dataState);
205
321
  }
206
322
  if (fieldValue !== void 0) {
207
323
  objectsToMerge.push({ [resultName]: fieldValue });
@@ -210,15 +326,60 @@ class StoreReader {
210
326
  else {
211
327
  const fragment = (0, internal_1.getFragmentFromSelection)(selection, context.lookupFragment);
212
328
  if (!fragment && selection.kind === graphql_1.Kind.FRAGMENT_SPREAD) {
213
- throw (0, invariant_1.newInvariantError)(113, selection.name.value);
329
+ throw (0, invariant_1.newInvariantError)(114, selection.name.value);
214
330
  }
215
331
  if (fragment && policies.fragmentMatches(fragment, typename)) {
216
- fragment.selectionSet.selections.forEach(workSet.add, workSet);
332
+ const isDeferBoundary = (0, internal_1.isDeferredFragment)(selection, context.variables);
333
+ // Prior to 4.3, this branch just flattened the fragment's
334
+ // selectionSet into the existing workSet so that it continued
335
+ // iterating as if the fragment didn't exist. The cache is
336
+ // incremental aware as of 4.3 and as such, we need to resolve the
337
+ // per-fragment selection set so that we can properly strip partial
338
+ // defer fragment data when returnPartialData is false. We need to
339
+ // call execSelectionSetImpl directly (non-cached version) so that we
340
+ // scope the dataState correctly for its fields. Using the cached
341
+ // executeSelectionSet can result in cache poisoning when combined
342
+ // with the fragment registry where it might cache either a) an error
343
+ // thrown when a registered fragment references a named fragment that
344
+ // the query is expected to supply and doesn't or b) resolve to the
345
+ // wrong data result when combined with queries that provide different
346
+ // implementations of the same fragment (see inmemory/fragmentRegistry and
347
+ // cache.diff/incremental tests which provide guards against this
348
+ // behavior).
349
+ const execResult = this.execSelectionSetImpl({
350
+ selectionSet: fragment.selectionSet,
351
+ objectOrReference,
352
+ enclosingRef,
353
+ context,
354
+ });
355
+ const { result, dataState: nextDataState } = execResult;
356
+ partialBoundaries.merge(execResult.partialBoundaries);
357
+ if (result !== void 0) {
358
+ objectsToMerge.push(result);
359
+ }
360
+ if (execResult.missing) {
361
+ missing = missingMerger.merge(missing, execResult.missing);
362
+ }
363
+ if (isDeferBoundary &&
364
+ (nextDataState === "partial" || nextDataState === "empty")) {
365
+ partialBoundaries.add(selection);
366
+ }
367
+ dataState = mergeDataState(dataState, isDeferBoundary ?
368
+ nextDataState === "empty" ? "streaming"
369
+ : nextDataState === "partial" ? "deferPartial"
370
+ : nextDataState
371
+ : nextDataState);
217
372
  }
218
373
  }
219
374
  });
375
+ dataState ||= "complete";
220
376
  const result = (0, internal_1.mergeDeepArray)(objectsToMerge);
221
- const finalResult = { result, missing };
377
+ const finalResult = {
378
+ result,
379
+ missing,
380
+ dataState,
381
+ partialBoundaries,
382
+ };
222
383
  const frozen = (0, internal_1.maybeDeepFreeze)(finalResult);
223
384
  // Store this result with its selection set so that we can quickly
224
385
  // recognize it again in the StoreReader#isFresh method.
@@ -229,8 +390,11 @@ class StoreReader {
229
390
  }
230
391
  // Uncached version of executeSubSelectedArray.
231
392
  execSubSelectedArrayImpl({ field, array, enclosingRef, context, }) {
393
+ let dataState = "complete";
232
394
  let missing;
233
395
  let missingMerger = new internal_1.DeepMerger();
396
+ const partialBoundaries = new PartialBoundaries();
397
+ const isStreamed = (0, internal_1.isStreamField)(field, context.variables);
234
398
  function handleMissing(childResult, i) {
235
399
  if (childResult.missing) {
236
400
  missing = missingMerger.merge(missing, { [i]: childResult.missing });
@@ -245,23 +409,36 @@ class StoreReader {
245
409
  if (item === null) {
246
410
  return null;
247
411
  }
412
+ let execResult;
248
413
  // This is a nested array, recurse
249
414
  if ((0, internal_1.isArray)(item)) {
250
- return handleMissing(this.executeSubSelectedArray({
415
+ execResult = this.executeSubSelectedArray({
251
416
  field,
252
417
  array: item,
253
418
  enclosingRef,
254
419
  context,
255
- }), i);
420
+ });
256
421
  }
257
- // This is an object, run the selection set on it
258
- if (field.selectionSet) {
259
- return handleMissing(this.executeSelectionSet({
422
+ else if (field.selectionSet) {
423
+ execResult = this.executeSelectionSet({
260
424
  selectionSet: field.selectionSet,
261
425
  objectOrReference: item,
262
426
  enclosingRef: (0, utilities_1.isReference)(item) ? item : enclosingRef,
263
427
  context,
264
- }), i);
428
+ });
429
+ }
430
+ if (execResult) {
431
+ const { dataState: nextDataState } = execResult;
432
+ partialBoundaries.set(i, nextDataState === "partial" ?
433
+ // avoid mutating the execResult partialBoundaries object
434
+ execResult.partialBoundaries.clone().add(field)
435
+ : execResult.partialBoundaries);
436
+ dataState = mergeDataState(dataState, isStreamed ?
437
+ nextDataState === "partial" ?
438
+ "streamPartial"
439
+ : nextDataState
440
+ : nextDataState);
441
+ return handleMissing(execResult, i);
265
442
  }
266
443
  if (environment_1.__DEV__) {
267
444
  assertSelectionSetForIdValue(context.store, field, item);
@@ -270,9 +447,162 @@ class StoreReader {
270
447
  });
271
448
  return {
272
449
  result: array,
450
+ dataState,
273
451
  missing,
452
+ partialBoundaries,
274
453
  };
275
454
  }
455
+ prunePartialBoundariesImpl({ boundaries, context, data, path, selectionSet, }) {
456
+ const { variables, lookupFragment, policies } = context;
457
+ if (data == null || !boundaries) {
458
+ return { result: data, dataState: "complete" };
459
+ }
460
+ const merger = new internal_1.DeepMerger();
461
+ let changed = false;
462
+ let dataState = "complete";
463
+ const result = {};
464
+ // __typename might not be part of the selection set, so preserve it when
465
+ // available, otherwise it gets removed since it's never visited when
466
+ // iterating the selection set.
467
+ if (Object.hasOwn(data, "__typename")) {
468
+ result.__typename = data.__typename;
469
+ }
470
+ const workSet = new Set(selectionSet.selections);
471
+ workSet.forEach((selection) => {
472
+ if (!(0, internal_1.shouldInclude)(selection, variables))
473
+ return;
474
+ if ((0, internal_1.isField)(selection)) {
475
+ const resultName = (0, internal_1.resultKeyNameFromField)(selection);
476
+ if (!Object.hasOwn(data, resultName)) {
477
+ return;
478
+ }
479
+ const fieldValue = data[resultName];
480
+ if (Array.isArray(fieldValue)) {
481
+ const pruned = this.prunePartialStreamArray({
482
+ field: selection,
483
+ array: fieldValue,
484
+ boundaries: boundaries.getChild(resultName),
485
+ context,
486
+ path: path.concat(resultName),
487
+ });
488
+ changed ||= pruned.result !== fieldValue;
489
+ result[resultName] = pruned.result;
490
+ dataState = mergeDataState(dataState, pruned.dataState);
491
+ }
492
+ else if (!selection.selectionSet) {
493
+ result[resultName] = fieldValue;
494
+ }
495
+ else {
496
+ const pruned = this.prunePartialBoundaries({
497
+ data: fieldValue,
498
+ selectionSet: selection.selectionSet,
499
+ boundaries: boundaries.getChild(resultName),
500
+ context,
501
+ path: path.concat(resultName),
502
+ });
503
+ changed ||= pruned.result !== fieldValue;
504
+ dataState = mergeDataState(dataState, pruned.dataState);
505
+ // A response key can be selected by more than one selection (e.g. a
506
+ // field and an overlapping fragment), so merge their kept fields.
507
+ result[resultName] =
508
+ Object.hasOwn(result, resultName) ?
509
+ merger.merge(result[resultName], pruned.result)
510
+ : pruned.result;
511
+ }
512
+ return;
513
+ }
514
+ // Note: we do NOT set `changed` to true anywhere in this branch of the
515
+ // conditional, despite the fact that we might have encountered a
516
+ // partial @defer boundary. Dropping a fragment does not guarantee keys
517
+ // are actually dropped which can happen when overlapping sibling
518
+ // selections contribute to the construction of the object. The final
519
+ // Object.keys(result).length check actually detects whether keys were
520
+ // dropped or not.
521
+ const fragment = (0, internal_1.getFragmentFromSelection)(selection, lookupFragment);
522
+ let prune = false;
523
+ if (context.deferInfo && (0, internal_1.isDeferredFragment)(selection, variables)) {
524
+ const directive = selection.directives?.find((d) => d.name.value === "defer");
525
+ const label = directive && (0, internal_1.getDirectiveArgValue)(directive, "label", graphql_1.Kind.STRING);
526
+ prune = !!context.deferInfo.peekArray(path.concat(label || []));
527
+ }
528
+ if (fragment && policies.fragmentMatches(fragment, data.__typename)) {
529
+ if (boundaries.has(selection) || prune) {
530
+ dataState = mergeDataState(dataState, "streaming");
531
+ }
532
+ else {
533
+ fragment.selectionSet.selections.forEach(workSet.add, workSet);
534
+ }
535
+ }
536
+ });
537
+ if (Object.keys(result).length !== Object.keys(data).length) {
538
+ changed = true;
539
+ }
540
+ else if (changed && boundaries.hasSelections()) {
541
+ // Overlapping siblings may rebuild the same fields under a new object
542
+ // identity (e.g. changed === true) after a partial @defer is skipped.
543
+ // We perform a deep equality check to verify whether anything was
544
+ // actually dropped by the partial @defer fragment.
545
+ changed = !(0, equality_1.equal)(result, data);
546
+ }
547
+ return { result: changed ? result : data, dataState };
548
+ }
549
+ prunePartialStreamArrayImpl({ field, array, boundaries, context, path, }) {
550
+ if (!boundaries)
551
+ return { result: array, dataState: "complete" };
552
+ let changed = false;
553
+ let dataState = "complete";
554
+ let pruned = [];
555
+ const state = context.streamInfo?.peekArray(path)?.state;
556
+ const length = Math.min(array.length, state?.truncate ? state.streamPosition : Number.MAX_SAFE_INTEGER);
557
+ for (let i = 0; i < length; i++) {
558
+ const item = array[i];
559
+ let prunedResult = {
560
+ result: item,
561
+ dataState: "complete",
562
+ };
563
+ const boundary = boundaries.getChild(i);
564
+ if (boundary?.has(field)) {
565
+ // The presence of streamInfo determines how we truncate partial
566
+ // stream arrays. Stream info is only given to cache.diff during
567
+ // in-flight requests so we want keep items in the array equal to the
568
+ // total that have streamed in (this is represented by streamPosition
569
+ // above). For all other cache reads, partial stream boundaries are
570
+ // pruned back to an empty array.
571
+ if (state) {
572
+ state.truncate = true;
573
+ pruned = pruned.slice(0, state.streamPosition);
574
+ }
575
+ else {
576
+ pruned = [];
577
+ dataState = "complete";
578
+ }
579
+ break;
580
+ }
581
+ if (Array.isArray(item)) {
582
+ prunedResult = this.prunePartialStreamArray({
583
+ field,
584
+ array: item,
585
+ boundaries: boundaries.getChild(i),
586
+ context,
587
+ path: path.concat(i),
588
+ });
589
+ }
590
+ else if (field.selectionSet) {
591
+ prunedResult = this.prunePartialBoundaries({
592
+ data: item,
593
+ selectionSet: field.selectionSet,
594
+ boundaries: boundaries.getChild(i),
595
+ context,
596
+ path: path.concat(i),
597
+ });
598
+ }
599
+ pruned.push(prunedResult.result);
600
+ changed ||= prunedResult.result !== item;
601
+ dataState = mergeDataState(dataState, prunedResult.dataState);
602
+ }
603
+ changed ||= pruned.length !== array.length;
604
+ return { result: changed ? pruned : array, dataState };
605
+ }
276
606
  }
277
607
  exports.StoreReader = StoreReader;
278
608
  function firstMissing(tree) {
@@ -294,7 +624,7 @@ function assertSelectionSetForIdValue(store, field, fieldValue) {
294
624
  if ((0, internal_1.isNonNullObject)(value)) {
295
625
  (0, invariant_1.invariant)(
296
626
  !(0, utilities_1.isReference)(value),
297
- 114,
627
+ 115,
298
628
  (0, helpers_js_1.getTypenameFromStoreObject)(store, value),
299
629
  field.name.value
300
630
  );
@@ -303,4 +633,102 @@ function assertSelectionSetForIdValue(store, field, fieldValue) {
303
633
  });
304
634
  }
305
635
  }
636
+ // We deliberately leave `returnPartialData` out of `executeSelectionSet`'s
637
+ // cache key. `isFresh` runs during writes and cannot provide a reliable value
638
+ // for this option, so including it would prevent a reliable cache hit.
639
+ //
640
+ // When `returnPartialData` is false, `diffQueryAgainstStore` prunes data from
641
+ // partial @defer boundaries after reading the cached result. `PartialBoundaries`
642
+ // records the selection paths needed for that pass, including empty nodes along
643
+ // a path. Overlapping non-deferred selections must still be rebuilt so fields
644
+ // contributed only by a partial deferred sibling are removed. The prune pass
645
+ // can then skip unrelated result branches.
646
+ class PartialBoundaries {
647
+ selections = new Set();
648
+ children = new Map();
649
+ add(selection) {
650
+ this.selections.add(selection);
651
+ return this;
652
+ }
653
+ has(selection) {
654
+ return this.selections.has(selection);
655
+ }
656
+ hasSelections() {
657
+ return this.selections.size > 0;
658
+ }
659
+ getChild(key) {
660
+ return this.children.get(key);
661
+ }
662
+ clone() {
663
+ return new PartialBoundaries().merge(this);
664
+ }
665
+ set(key, boundary) {
666
+ const child = this.getChild(key);
667
+ this.children.set(key,
668
+ // Create a new PartialBoundaries instance to avoid mutating any cached
669
+ // execResult partialBoundaries objects
670
+ child ? child.clone().merge(boundary) : boundary);
671
+ }
672
+ merge(boundaries) {
673
+ boundaries.selections.forEach((selection) => this.add(selection));
674
+ boundaries.children.forEach((child, key) => this.set(key, child));
675
+ return this;
676
+ }
677
+ }
678
+ function shouldPrune({ dataState }, context) {
679
+ if (dataState === "deferPartial" || dataState === "streamPartial") {
680
+ return true;
681
+ }
682
+ if (dataState === "complete" || dataState === "streaming") {
683
+ return !!(
684
+ // If the last cache write repaired a partial @stream array to a
685
+ // complete array, the stream array might contain stale entries after
686
+ // the last written value. We only want to deliver the results up to
687
+ // the index the network wrote so we need to prune it too.
688
+ (context.streamInfo ||
689
+ // The network hasn't delivered these @defer boundaries yet, so prune
690
+ // the (possibly complete) cached data sitting at them.
691
+ context.deferInfo));
692
+ }
693
+ return false;
694
+ }
695
+ // Describes the data state transitions that change the running state when it's
696
+ // combined with the next data state. Omitted object values represent
697
+ // "impossible" merges where the data state should remain the same.
698
+ const DATA_STATE_MERGES = {
699
+ empty: {
700
+ complete: "partial",
701
+ deferPartial: "partial",
702
+ streaming: "partial",
703
+ streamPartial: "partial",
704
+ },
705
+ deferPartial: {
706
+ empty: "partial",
707
+ },
708
+ streamPartial: {
709
+ deferPartial: "deferPartial",
710
+ empty: "partial",
711
+ },
712
+ streaming: {
713
+ deferPartial: "deferPartial",
714
+ empty: "partial",
715
+ streamPartial: "streamPartial",
716
+ },
717
+ complete: {
718
+ deferPartial: "deferPartial",
719
+ streaming: "streaming",
720
+ streamPartial: "streamPartial",
721
+ empty: "partial",
722
+ },
723
+ partial: {},
724
+ };
725
+ function mergeDataState(current, next) {
726
+ if (next === "partial") {
727
+ return "partial";
728
+ }
729
+ if (!current || current === next) {
730
+ return next;
731
+ }
732
+ return DATA_STATE_MERGES[current][next] || current;
733
+ }
306
734
  //# sourceMappingURL=readFromStore.cjs.map