@apollo/client 4.3.0-alpha.6 → 4.3.0-alpha.8

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 (140) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/__cjs/cache/core/cache.cjs +25 -1
  3. package/__cjs/cache/core/cache.cjs.map +1 -1
  4. package/__cjs/cache/core/cache.d.cts +19 -1
  5. package/__cjs/cache/inmemory/entityStore.cjs +3 -3
  6. package/__cjs/cache/inmemory/inMemoryCache.cjs +10 -0
  7. package/__cjs/cache/inmemory/inMemoryCache.cjs.map +1 -1
  8. package/__cjs/cache/inmemory/inMemoryCache.d.cts +5 -1
  9. package/__cjs/cache/inmemory/key-extractor.cjs +1 -1
  10. package/__cjs/cache/inmemory/policies.cjs +5 -5
  11. package/__cjs/cache/inmemory/readFromStore.cjs +3 -3
  12. package/__cjs/cache/inmemory/writeToStore.cjs +4 -4
  13. package/__cjs/core/ApolloClient.cjs +14 -14
  14. package/__cjs/core/ObservableQuery.cjs +173 -61
  15. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  16. package/__cjs/core/ObservableQuery.d.cts +3 -6
  17. package/__cjs/core/QueryInfo.cjs +11 -125
  18. package/__cjs/core/QueryInfo.cjs.map +1 -1
  19. package/__cjs/core/QueryInfo.d.cts +0 -24
  20. package/__cjs/core/QueryManager.cjs +23 -15
  21. package/__cjs/core/QueryManager.cjs.map +1 -1
  22. package/__cjs/core/RefetchEventManager.cjs +3 -3
  23. package/__cjs/incremental/handlers/graphql17Alpha9.cjs +1 -1
  24. package/__cjs/incremental/handlers/notImplemented.cjs +1 -1
  25. package/__cjs/invariantErrorCodes.cjs +141 -115
  26. package/__cjs/link/core/ApolloLink.cjs +3 -3
  27. package/__cjs/link/http/checkFetcher.cjs +1 -1
  28. package/__cjs/link/http/parseAndCheckHttpResponse.cjs +2 -2
  29. package/__cjs/link/http/parseAndCheckHttpResponse.cjs.map +1 -1
  30. package/__cjs/link/persisted-queries/index.cjs +2 -2
  31. package/__cjs/link/ws/index.cjs +1 -1
  32. package/__cjs/local-state/LocalState.cjs +11 -11
  33. package/__cjs/masking/maskDefinition.cjs +2 -2
  34. package/__cjs/masking/maskFragment.cjs +2 -2
  35. package/__cjs/masking/maskOperation.cjs +1 -1
  36. package/__cjs/masking/utils.cjs +3 -3
  37. package/__cjs/react/context/ApolloContext.cjs +1 -1
  38. package/__cjs/react/context/ApolloProvider.cjs +1 -1
  39. package/__cjs/react/hooks/internal/validateSuspenseHookOptions.cjs +2 -2
  40. package/__cjs/react/hooks/useApolloClient.cjs +1 -1
  41. package/__cjs/react/hooks/useLazyQuery.cjs +2 -2
  42. package/__cjs/react/hooks/useLoadableQuery.cjs +2 -2
  43. package/__cjs/react/hooks/useSubscription.cjs +1 -1
  44. package/__cjs/react/hooks/useSyncExternalStore.cjs +1 -1
  45. package/__cjs/react/internal/cache/QueryReference.cjs +1 -1
  46. package/__cjs/react/ssr/prerenderStatic.cjs +2 -2
  47. package/__cjs/testing/core/mocking/mockLink.cjs +4 -4
  48. package/__cjs/utilities/graphql/DocumentTransform.cjs +1 -1
  49. package/__cjs/utilities/internal/capitalize.cjs +12 -0
  50. package/__cjs/utilities/internal/capitalize.cjs.map +1 -0
  51. package/__cjs/utilities/internal/capitalize.d.cts +7 -0
  52. package/__cjs/utilities/internal/coerceScalarFieldsToParsed.cjs +69 -0
  53. package/__cjs/utilities/internal/coerceScalarFieldsToParsed.cjs.map +1 -0
  54. package/__cjs/utilities/internal/coerceScalarFieldsToParsed.d.cts +9 -0
  55. package/__cjs/utilities/internal/getFragmentDefinition.cjs +3 -3
  56. package/__cjs/utilities/internal/getFragmentFromSelection.cjs +1 -1
  57. package/__cjs/utilities/internal/getFragmentFromSelection.cjs.map +1 -1
  58. package/__cjs/utilities/internal/getFragmentQueryDocument.cjs +2 -2
  59. package/__cjs/utilities/internal/getMainDefinition.cjs +1 -1
  60. package/__cjs/utilities/internal/getQueryDefinition.cjs +1 -1
  61. package/__cjs/utilities/internal/index.cjs +6 -2
  62. package/__cjs/utilities/internal/index.cjs.map +1 -1
  63. package/__cjs/utilities/internal/index.d.cts +2 -0
  64. package/__cjs/utilities/internal/removeDirectivesFromDocument.cjs +1 -1
  65. package/__cjs/utilities/internal/shouldInclude.cjs +4 -4
  66. package/__cjs/utilities/internal/valueToObjectRepresentation.cjs +1 -1
  67. package/__cjs/version.cjs +1 -1
  68. package/cache/core/cache.d.ts +19 -1
  69. package/cache/core/cache.js +26 -2
  70. package/cache/core/cache.js.map +1 -1
  71. package/cache/inmemory/entityStore.js +3 -3
  72. package/cache/inmemory/inMemoryCache.d.ts +5 -1
  73. package/cache/inmemory/inMemoryCache.js +10 -0
  74. package/cache/inmemory/inMemoryCache.js.map +1 -1
  75. package/cache/inmemory/key-extractor.js +1 -1
  76. package/cache/inmemory/policies.js +5 -5
  77. package/cache/inmemory/readFromStore.js +3 -3
  78. package/cache/inmemory/writeToStore.js +4 -4
  79. package/core/ApolloClient.js +14 -14
  80. package/core/ObservableQuery.d.ts +3 -6
  81. package/core/ObservableQuery.js +169 -61
  82. package/core/ObservableQuery.js.map +1 -1
  83. package/core/QueryInfo.d.ts +1 -25
  84. package/core/QueryInfo.js +11 -125
  85. package/core/QueryInfo.js.map +1 -1
  86. package/core/QueryManager.js +24 -16
  87. package/core/QueryManager.js.map +1 -1
  88. package/core/RefetchEventManager.js +3 -3
  89. package/incremental/handlers/graphql17Alpha9.js +1 -1
  90. package/incremental/handlers/notImplemented.js +1 -1
  91. package/invariantErrorCodes.js +141 -115
  92. package/link/core/ApolloLink.js +3 -3
  93. package/link/http/checkFetcher.js +1 -1
  94. package/link/http/parseAndCheckHttpResponse.js +2 -2
  95. package/link/http/parseAndCheckHttpResponse.js.map +1 -1
  96. package/link/persisted-queries/index.js +2 -2
  97. package/link/ws/index.js +1 -1
  98. package/local-state/LocalState.js +11 -11
  99. package/masking/maskDefinition.js +2 -2
  100. package/masking/maskFragment.js +2 -2
  101. package/masking/maskOperation.js +1 -1
  102. package/masking/utils.js +3 -3
  103. package/package.json +1 -1
  104. package/react/context/ApolloContext.js +1 -1
  105. package/react/context/ApolloProvider.js +1 -1
  106. package/react/hooks/internal/validateSuspenseHookOptions.js +2 -2
  107. package/react/hooks/useApolloClient.js +1 -1
  108. package/react/hooks/useLazyQuery.js +2 -2
  109. package/react/hooks/useLoadableQuery.js +2 -2
  110. package/react/hooks/useSubscription.js +1 -1
  111. package/react/hooks/useSyncExternalStore.js +1 -1
  112. package/react/hooks-compiled/internal/validateSuspenseHookOptions.js +2 -2
  113. package/react/hooks-compiled/useApolloClient.js +1 -1
  114. package/react/hooks-compiled/useLazyQuery.js +2 -2
  115. package/react/hooks-compiled/useLoadableQuery.js +2 -2
  116. package/react/hooks-compiled/useSubscription.js +1 -1
  117. package/react/hooks-compiled/useSyncExternalStore.js +1 -1
  118. package/react/internal/cache/QueryReference.js +1 -1
  119. package/react/ssr/prerenderStatic.js +2 -2
  120. package/testing/core/mocking/mockLink.js +4 -4
  121. package/utilities/graphql/DocumentTransform.js +1 -1
  122. package/utilities/internal/capitalize.d.ts +7 -0
  123. package/utilities/internal/capitalize.js +9 -0
  124. package/utilities/internal/capitalize.js.map +1 -0
  125. package/utilities/internal/coerceScalarFieldsToParsed.d.ts +9 -0
  126. package/utilities/internal/coerceScalarFieldsToParsed.js +66 -0
  127. package/utilities/internal/coerceScalarFieldsToParsed.js.map +1 -0
  128. package/utilities/internal/getFragmentDefinition.js +3 -3
  129. package/utilities/internal/getFragmentFromSelection.js +1 -1
  130. package/utilities/internal/getFragmentFromSelection.js.map +1 -1
  131. package/utilities/internal/getFragmentQueryDocument.js +2 -2
  132. package/utilities/internal/getMainDefinition.js +1 -1
  133. package/utilities/internal/getQueryDefinition.js +1 -1
  134. package/utilities/internal/index.d.ts +2 -0
  135. package/utilities/internal/index.js +2 -0
  136. package/utilities/internal/index.js.map +1 -1
  137. package/utilities/internal/removeDirectivesFromDocument.js +1 -1
  138. package/utilities/internal/shouldInclude.js +4 -4
  139. package/utilities/internal/valueToObjectRepresentation.js +1 -1
  140. package/version.js +1 -1
@@ -251,7 +251,7 @@ class Policies {
251
251
  const rootId = "ROOT_" + which.toUpperCase();
252
252
  const old = this.rootTypenamesById[rootId];
253
253
  if (typename !== old) {
254
- (0, invariant_1.invariant)(!old || old === which, 109, which);
254
+ (0, invariant_1.invariant)(!old || old === which, 111, which);
255
255
  // First, delete any old __typename associated with this rootId from
256
256
  // rootIdsByTypename.
257
257
  if (old)
@@ -394,7 +394,7 @@ class Policies {
394
394
  if (supertypeSet.has(supertype)) {
395
395
  if (!typenameSupertypeSet.has(supertype)) {
396
396
  if (checkingFuzzySubtypes) {
397
- __DEV__ && invariant_1.invariant.warn(110, typename, supertype);
397
+ __DEV__ && invariant_1.invariant.warn(112, typename, supertype);
398
398
  }
399
399
  // Record positive results for faster future lookup.
400
400
  // Unfortunately, we cannot safely cache negative results,
@@ -651,7 +651,7 @@ function normalizeReadFieldOptions(readFieldArgs, objectOrReference, variables)
651
651
  }
652
652
  }
653
653
  if (environment_1.__DEV__ && options.from === void 0) {
654
- __DEV__ && invariant_1.invariant.warn(111, (0, internal_2.stringifyForDisplay)(Array.from(readFieldArgs)));
654
+ __DEV__ && invariant_1.invariant.warn(113, (0, internal_2.stringifyForDisplay)(Array.from(readFieldArgs)));
655
655
  }
656
656
  if (void 0 === options.variables) {
657
657
  options.variables = variables;
@@ -661,7 +661,7 @@ function normalizeReadFieldOptions(readFieldArgs, objectOrReference, variables)
661
661
  function makeMergeObjectsFunction(store) {
662
662
  return function mergeObjects(existing, incoming) {
663
663
  if ((0, internal_2.isArray)(existing) || (0, internal_2.isArray)(incoming)) {
664
- throw (0, invariant_1.newInvariantError)(112);
664
+ throw (0, invariant_1.newInvariantError)(114);
665
665
  }
666
666
  // These dynamic checks are necessary because the parameters of a
667
667
  // custom merge function can easily have the any type, so the type
@@ -698,6 +698,6 @@ function makeMergeObjectsFunction(store) {
698
698
  };
699
699
  }
700
700
  function warnAboutScalarConfig(typename, fieldName, scalar, kind) {
701
- __DEV__ && invariant_1.invariant.warn(113, `${typename}.${fieldName}`, scalar, kind);
701
+ __DEV__ && invariant_1.invariant.warn(115, `${typename}.${fieldName}`, scalar, kind);
702
702
  }
703
703
  //# sourceMappingURL=policies.cjs.map
@@ -289,7 +289,7 @@ class StoreReader {
289
289
  if (typename) {
290
290
  const policy = policies["getFieldPolicy"](typename, fieldName);
291
291
  if (policy?.scalar) {
292
- __DEV__ && invariant_1.invariant.warn(114, `${typename}.${fieldName}`, policy.scalar);
292
+ __DEV__ && invariant_1.invariant.warn(116, `${typename}.${fieldName}`, policy.scalar);
293
293
  }
294
294
  }
295
295
  }
@@ -326,7 +326,7 @@ class StoreReader {
326
326
  else {
327
327
  const fragment = (0, internal_1.getFragmentFromSelection)(selection, context.lookupFragment);
328
328
  if (!fragment && selection.kind === graphql_1.Kind.FRAGMENT_SPREAD) {
329
- throw (0, invariant_1.newInvariantError)(115, selection.name.value);
329
+ throw (0, invariant_1.newInvariantError)(117, selection.name.value);
330
330
  }
331
331
  if (fragment && policies.fragmentMatches(fragment, typename)) {
332
332
  const isDeferBoundary = (0, internal_1.isDeferredFragment)(selection, context.variables);
@@ -624,7 +624,7 @@ function assertSelectionSetForIdValue(store, field, fieldValue) {
624
624
  if ((0, internal_1.isNonNullObject)(value)) {
625
625
  (0, invariant_1.invariant)(
626
626
  !(0, utilities_1.isReference)(value),
627
- 116,
627
+ 118,
628
628
  (0, helpers_js_1.getTypenameFromStoreObject)(store, value),
629
629
  field.name.value
630
630
  );
@@ -75,7 +75,7 @@ class StoreWriter {
75
75
  path: [],
76
76
  });
77
77
  if (!(0, utilities_1.isReference)(ref)) {
78
- throw (0, invariant_1.newInvariantError)(117, result);
78
+ throw (0, invariant_1.newInvariantError)(119, result);
79
79
  }
80
80
  // So far, the store has not been modified, so now it's time to process
81
81
  // context.incomingById and merge those incoming fields into context.store.
@@ -237,7 +237,7 @@ class StoreWriter {
237
237
  // provide a default value, so its absence from the written data should
238
238
  // not be cause for alarm.
239
239
  !policies.getReadFunction(typename, field.name.value)) {
240
- invariant_1.invariant.error(118, (0, internal_1.resultKeyNameFromField)(field), result);
240
+ invariant_1.invariant.error(120, (0, internal_1.resultKeyNameFromField)(field), result);
241
241
  }
242
242
  });
243
243
  // Identify the result object, even if dataId was already provided,
@@ -385,7 +385,7 @@ class StoreWriter {
385
385
  else {
386
386
  const fragment = (0, internal_1.getFragmentFromSelection)(selection, context.lookupFragment);
387
387
  if (!fragment && selection.kind === graphql_1.Kind.FRAGMENT_SPREAD) {
388
- throw (0, invariant_1.newInvariantError)(119, selection.name.value);
388
+ throw (0, invariant_1.newInvariantError)(121, selection.name.value);
389
389
  }
390
390
  if (fragment &&
391
391
  policies.fragmentMatches(fragment, typename, result, context.variables)) {
@@ -558,7 +558,7 @@ function warnAboutDataLoss(existingRef, incomingObj, storeFieldName, store) {
558
558
  }
559
559
  });
560
560
  }
561
- __DEV__ && invariant_1.invariant.warn(120, fieldName, parentType, childTypenames.length ?
561
+ __DEV__ && invariant_1.invariant.warn(122, fieldName, parentType, childTypenames.length ?
562
562
  "either ensure all objects of type " +
563
563
  childTypenames.join(" and ") +
564
564
  " have an ID or a custom merge function, or "
@@ -78,8 +78,8 @@ class ApolloClient {
78
78
  */
79
79
  constructor(options) {
80
80
  if (environment_1.__DEV__) {
81
- (0, invariant_1.invariant)(options.cache, 68);
82
- (0, invariant_1.invariant)(options.link, 69);
81
+ (0, invariant_1.invariant)(options.cache, 69);
82
+ (0, invariant_1.invariant)(options.link, 70);
83
83
  }
84
84
  const { cache, documentTransform, ssrMode = false, ssrForceFetchDelay = 0, queryDeduplication = true, defaultOptions, defaultContext, assumeImmutableResults = cache.assumeImmutableResults, localState, devtools, dataMasking, link, incrementalHandler = new incremental_1.NotImplementedHandler(), experiments = [], refetchEventManager, } = options;
85
85
  this.link = link;
@@ -267,12 +267,12 @@ class ApolloClient {
267
267
  if (refetchOn) {
268
268
  const operationName = (0, internal_1.getOperationName)(query, "(anonymous)");
269
269
  if (!refetchEventManager) {
270
- __DEV__ && invariant_1.invariant.warn(70, operationName);
270
+ __DEV__ && invariant_1.invariant.warn(71, operationName);
271
271
  }
272
272
  else if (typeof refetchOn === "object") {
273
273
  Object.keys(refetchOn).forEach((source) => {
274
274
  if (!refetchEventManager.hasSource(source)) {
275
- __DEV__ && invariant_1.invariant.warn(71, source, operationName);
275
+ __DEV__ && invariant_1.invariant.warn(72, source, operationName);
276
276
  }
277
277
  });
278
278
  }
@@ -294,13 +294,13 @@ class ApolloClient {
294
294
  options = (0, internal_1.mergeOptions)(this.defaultOptions.query, options);
295
295
  }
296
296
  if (environment_1.__DEV__) {
297
- (0, invariant_1.invariant)(options.fetchPolicy !== "cache-and-network", 72);
298
- (0, invariant_1.invariant)(options.fetchPolicy !== "standby", 73);
299
- (0, invariant_1.invariant)(options.query, 74);
300
- (0, invariant_1.invariant)(options.query.kind === "Document", 75);
301
- (0, invariant_1.invariant)(!options.returnPartialData, 76);
302
- (0, invariant_1.invariant)(!options.pollInterval, 77);
303
- (0, invariant_1.invariant)(!options.notifyOnNetworkStatusChange, 78);
297
+ (0, invariant_1.invariant)(options.fetchPolicy !== "cache-and-network", 73);
298
+ (0, invariant_1.invariant)(options.fetchPolicy !== "standby", 74);
299
+ (0, invariant_1.invariant)(options.query, 75);
300
+ (0, invariant_1.invariant)(options.query.kind === "Document", 76);
301
+ (0, invariant_1.invariant)(!options.returnPartialData, 77);
302
+ (0, invariant_1.invariant)(!options.pollInterval, 78);
303
+ (0, invariant_1.invariant)(!options.notifyOnNetworkStatusChange, 79);
304
304
  }
305
305
  return this.queryManager.query(options);
306
306
  };
@@ -318,9 +318,9 @@ class ApolloClient {
318
318
  errorPolicy: "none",
319
319
  }, this.defaultOptions.mutate), options);
320
320
  if (environment_1.__DEV__) {
321
- (0, invariant_1.invariant)(optionsWithDefaults.mutation, 79);
321
+ (0, invariant_1.invariant)(optionsWithDefaults.mutation, 80);
322
322
  (0, invariant_1.invariant)(optionsWithDefaults.fetchPolicy === "network-only" ||
323
- optionsWithDefaults.fetchPolicy === "no-cache", 80);
323
+ optionsWithDefaults.fetchPolicy === "no-cache", 81);
324
324
  }
325
325
  (0, internal_1.checkDocument)(optionsWithDefaults.mutation, graphql_1.OperationTypeNode.MUTATION);
326
326
  return this.queryManager.mutate(optionsWithDefaults);
@@ -526,7 +526,7 @@ class ApolloClient {
526
526
  // result.queries and result.results instead, you shouldn't have to worry
527
527
  // about preventing uncaught rejections for the Promise.all result.
528
528
  result.catch((error) => {
529
- __DEV__ && invariant_1.invariant.debug(81, error);
529
+ __DEV__ && invariant_1.invariant.debug(82, error);
530
530
  });
531
531
  return result;
532
532
  }
@@ -29,16 +29,29 @@ const empty = {
29
29
  dataState: "empty",
30
30
  partial: true,
31
31
  };
32
+ const destructiveMethodCounts = new WeakMap();
33
+ function wrapDestructiveCacheMethod(cache, methodName) {
34
+ const original = cache[methodName];
35
+ if (typeof original === "function") {
36
+ // @ts-expect-error this is just too generic to be typed correctly
37
+ cache[methodName] = function () {
38
+ destructiveMethodCounts.set(cache,
39
+ // The %1e15 allows the count to wrap around to 0 safely every
40
+ // quadrillion evictions, so there's no risk of overflow. To be
41
+ // clear, this is more of a pedantic principle than something
42
+ // that matters in any conceivable practical scenario.
43
+ (destructiveMethodCounts.get(cache) + 1) % 1e15);
44
+ // @ts-expect-error this is just too generic to be typed correctly
45
+ return original.apply(this, arguments);
46
+ };
47
+ }
48
+ }
32
49
  class ObservableQuery {
33
50
  options;
34
51
  queryName;
35
52
  variablesUnknown = false;
36
- /**
37
- * @internal will be read and written from `QueryInfo`
38
- *
39
- * @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
40
- */
41
- _lastWrite;
53
+ didWarnOnFeud = false;
54
+ lastMissing;
42
55
  // The `query` computed property will always reflect the document transformed
43
56
  // by the last run query. `this.options.query` will always reflect the raw
44
57
  // untransformed query to ensure document transforms with runtime conditionals
@@ -75,6 +88,18 @@ class ObservableQuery {
75
88
  }
76
89
  constructor({ queryManager, options, transformedQuery = queryManager.transform(options.query), }) {
77
90
  this.queryManager = queryManager;
91
+ // Track how often destructive cache methods are called, since we want
92
+ // eviction to override the feud-stopping logic in `shouldAutoRefetch`,
93
+ // by causing it to return true. Wrapping these cache methods is a bit of a
94
+ // hack, but it saves us from having to make eviction counting an official
95
+ // part of the ApolloCache API.
96
+ const { cache } = queryManager;
97
+ if (!destructiveMethodCounts.has(cache)) {
98
+ destructiveMethodCounts.set(cache, 0);
99
+ wrapDestructiveCacheMethod(cache, "evict");
100
+ wrapDestructiveCacheMethod(cache, "modify");
101
+ wrapDestructiveCacheMethod(cache, "reset");
102
+ }
78
103
  // active state
79
104
  this.waitForNetworkResult = options.fetchPolicy === "network-only";
80
105
  this.isTornDown = false;
@@ -85,7 +110,7 @@ class ObservableQuery {
85
110
  // Make sure we don't store "standby" as the initialFetchPolicy.
86
111
  initialFetchPolicy = fetchPolicy === "standby" ? defaultFetchPolicy : (fetchPolicy), } = options;
87
112
  if (options[internal_1.variablesUnknownSymbol]) {
88
- (0, invariant_1.invariant)(fetchPolicy === "standby", 82);
113
+ (0, invariant_1.invariant)(fetchPolicy === "standby", 83);
89
114
  this.variablesUnknown = true;
90
115
  }
91
116
  this.lastQuery = transformedQuery;
@@ -394,7 +419,7 @@ class ObservableQuery {
394
419
  const queryDef = (0, internal_1.getQueryDefinition)(this.query);
395
420
  const vars = queryDef.variableDefinitions;
396
421
  if (!vars || !vars.some((v) => v.variable.name.value === "variables")) {
397
- __DEV__ && invariant_1.invariant.warn(83, variables, queryDef.name?.value || queryDef);
422
+ __DEV__ && invariant_1.invariant.warn(84, variables, queryDef.name?.value || queryDef);
398
423
  }
399
424
  }
400
425
  if (variables && !(0, equality_1.equal)(this.variables, variables)) {
@@ -402,7 +427,6 @@ class ObservableQuery {
402
427
  reobserveOptions.variables = this.options.variables =
403
428
  this.getVariablesWithDefaults({ ...this.variables, ...variables });
404
429
  }
405
- this._lastWrite = undefined;
406
430
  return this._reobserve(reobserveOptions, {
407
431
  newNetworkStatus: networkStatus_js_1.NetworkStatus.refetch,
408
432
  });
@@ -410,7 +434,7 @@ class ObservableQuery {
410
434
  fetchMore({ query, variables, context, errorPolicy, updateQuery, }) {
411
435
  (0, invariant_1.invariant)(
412
436
  this.options.fetchPolicy !== "cache-only",
413
- 84,
437
+ 85,
414
438
  (0, internal_1.getOperationName)(this.query, "(anonymous)")
415
439
  );
416
440
  const combinedOptions = {
@@ -444,7 +468,7 @@ class ObservableQuery {
444
468
  let wasUpdated = false;
445
469
  const isCached = this.options.fetchPolicy !== "no-cache";
446
470
  if (!isCached) {
447
- (0, invariant_1.invariant)(updateQuery, 85);
471
+ (0, invariant_1.invariant)(updateQuery, 86);
448
472
  }
449
473
  const { finalize, pushNotification } = this.pushOperation(networkStatus_js_1.NetworkStatus.fetchMore);
450
474
  pushNotification({
@@ -618,7 +642,7 @@ class ObservableQuery {
618
642
  onError(error);
619
643
  }
620
644
  else {
621
- invariant_1.invariant.error(86, error);
645
+ invariant_1.invariant.error(87, error);
622
646
  }
623
647
  return;
624
648
  }
@@ -869,7 +893,7 @@ class ObservableQuery {
869
893
  if (!this.didWarnCacheOnlyPolling &&
870
894
  pollInterval &&
871
895
  fetchPolicy === "cache-only") {
872
- __DEV__ && invariant_1.invariant.warn(87, (0, internal_1.getOperationName)(this.query, "(anonymous)"));
896
+ __DEV__ && invariant_1.invariant.warn(88, (0, internal_1.getOperationName)(this.query, "(anonymous)"));
873
897
  this.didWarnCacheOnlyPolling = true;
874
898
  }
875
899
  }
@@ -890,7 +914,6 @@ class ObservableQuery {
890
914
  if (this.pollingInfo) {
891
915
  if (!(0, utilities_1.isNetworkRequestInFlight)(this.networkStatus) &&
892
916
  !this.options.skipPollAttempt?.()) {
893
- this._lastWrite = undefined;
894
917
  this._reobserve({
895
918
  // Most fetchPolicy options don't make sense to use in a polling context, as
896
919
  // users wouldn't want to be polling the cache directly. However, network-only and
@@ -936,7 +959,10 @@ class ObservableQuery {
936
959
  }
937
960
  _reobserve(newOptions, internalOptions) {
938
961
  this.isTornDown = false;
939
- let { newNetworkStatus } = internalOptions || {};
962
+ let { newNetworkStatus, keepLastMissing } = internalOptions || {};
963
+ if (!keepLastMissing) {
964
+ this.lastMissing = undefined;
965
+ }
940
966
  this.queryManager.obsQueries.add(this);
941
967
  const useDisposableObservable =
942
968
  // Refetching uses a disposable Observable to allow refetches using different
@@ -1077,7 +1103,7 @@ class ObservableQuery {
1077
1103
  this.queryManager.obsQueries.delete(this);
1078
1104
  this.isTornDown = true;
1079
1105
  this.abortActiveOperations();
1080
- this._lastWrite = undefined;
1106
+ this.lastMissing = undefined;
1081
1107
  }
1082
1108
  transformDocument(document) {
1083
1109
  return this.queryManager.transform(document);
@@ -1135,51 +1161,126 @@ class ObservableQuery {
1135
1161
  return;
1136
1162
  }
1137
1163
  }
1138
- const { dirty } = this;
1164
+ const { dirty, lastMissing } = this;
1165
+ const { fetchPolicy } = this.options;
1139
1166
  this.resetNotifications();
1140
- if (dirty &&
1141
- (this.options.fetchPolicy === "cache-only" ||
1142
- this.options.fetchPolicy === "cache-and-network" ||
1143
- !this.activeOperations.size)) {
1144
- const diff = this.getCacheDiff();
1145
- if (
1146
- // `fromOptimisticTransaction` is not available through the `cache.diff`
1147
- // code path, so we need to check it this way
1148
- (0, equality_1.equal)(diff.result, this.getCacheDiff({ optimistic: false }).result)) {
1149
- //If this diff did not come from an optimistic transaction
1150
- // make the ObservableQuery "reobserve" the latest data
1151
- // using a temporary fetch policy of "cache-first", so complete cache
1152
- // results have a chance to be delivered without triggering additional
1153
- // network requests, even when options.fetchPolicy is "network-only"
1154
- // or "cache-and-network". All other fetch policies are preserved by
1155
- // this method, and are handled by calling oq.reobserve(). If this
1156
- // reobservation is spurious, distinctUntilChanged still has a
1157
- // chance to catch it before delivery to ObservableQuery subscribers.
1158
- this.reobserveCacheFirst();
1167
+ if (!dirty ||
1168
+ (fetchPolicy !== "cache-only" &&
1169
+ fetchPolicy !== "cache-and-network" &&
1170
+ this.activeOperations.size)) {
1171
+ return;
1172
+ }
1173
+ const diff = this.getCacheDiff();
1174
+ const current = this.getCurrentResult();
1175
+ // `fromOptimisticTransaction` is not available through the `cache.diff`
1176
+ // code path, so we need to check whether the cache result is an optimistic
1177
+ // result this way.
1178
+ const isOptimistic = !(0, equality_1.equal)(diff.result, this.getCacheDiff({ optimistic: false }).result);
1179
+ if (
1180
+ // When this diff came from an optimistic transaction, deliver the
1181
+ // current cache data to the ObservableQuery, but don't perform a
1182
+ // reobservation, since oq.reobserveCacheFirst might make a network
1183
+ // request, and we never want to trigger network requests in the
1184
+ // middle of optimistic updates.
1185
+ isOptimistic ||
1186
+ // If we get a cache update in the middle of streaming (possible with
1187
+ // cache-and-network fetch policy), just deliver the cache value without
1188
+ // going through the full reobserve which would otherwise trigger another
1189
+ // request (deduplication should kick in, but doing so replays any
1190
+ // previous emits from the link chain, which get rewritten into the cache
1191
+ // and might clobber this cache update)
1192
+ (!diff.complete && current.networkStatus === networkStatus_js_1.NetworkStatus.streaming)) {
1193
+ this.deliverCacheDiff(diff);
1194
+ return;
1195
+ }
1196
+ if (diff.complete) {
1197
+ this.lastMissing = undefined;
1198
+ }
1199
+ else if (!lastMissing ||
1200
+ // If a destructive cache method has been called since the last recorded
1201
+ // incomplete result, there's a chance fetching this data again will
1202
+ // restore what was evicted, even though the cache result looks the same
1203
+ // as before.
1204
+ lastMissing.dmCount !== destructiveMethodCounts.get(this.cache) ||
1205
+ !(0, equality_1.equal)(lastMissing.variables, this.variables) ||
1206
+ !(0, equality_1.equal)(lastMissing.missing, diff.missing?.missing)) {
1207
+ this.didWarnOnFeud = false;
1208
+ this.lastMissing = {
1209
+ variables: this.variables,
1210
+ missing: diff.missing?.missing,
1211
+ dmCount: destructiveMethodCounts.get(this.cache),
1212
+ };
1213
+ }
1214
+ else if (
1215
+ // reobserveCacheFirst with cache-only fetch policy only calls
1216
+ // reobserve which never fetches from the network so we are ok
1217
+ // allowing cache-only queries to fallthrough to reobserveCacheFirst.
1218
+ // This also prevents the feud warning which would be confusing for a
1219
+ // cache-only query anyways.
1220
+ fetchPolicy !== "cache-only") {
1221
+ // If we've fallen through to this case, a cache emit has returned the
1222
+ // same missing fields which means at least one value on the fields we've
1223
+ // already delivered have changed. We are ok delivering the updated
1224
+ // partial result in this case to keep the result as fresh as possible. We
1225
+ // NEVER want to downgrade this query from a complete query to a partial
1226
+ // query though, so we also make sure we only deliver if the previous
1227
+ // result was also partial.
1228
+ if (current.dataState === "partial") {
1229
+ this.deliverCacheDiff(diff);
1159
1230
  }
1160
- else {
1161
- // If this diff came from an optimistic transaction, deliver the
1162
- // current cache data to the ObservableQuery, but don't perform a
1163
- // reobservation, since oq.reobserveCacheFirst might make a network
1164
- // request, and we never want to trigger network requests in the
1165
- // middle of optimistic updates.
1166
- this.input.next({
1167
- kind: "N",
1168
- value: {
1169
- data: diff.result,
1170
- dataState: diff.dataState,
1171
- networkStatus: networkStatus_js_1.NetworkStatus.ready,
1172
- loading: false,
1173
- error: undefined,
1174
- partial: !diff.complete,
1175
- },
1176
- source: "cache",
1177
- query: this.query,
1178
- variables: this.variables,
1179
- meta: {},
1180
- });
1231
+ // If the (partial) result is the same as the last partial result
1232
+ // we recorded from a previous broadcast (and the variables match
1233
+ // too), avoid calling reobserveCacheFirst to refetch this query
1234
+ // again. If we allow refetching anytime this result becomes partial,
1235
+ // we risk feuds between queries competing to update the same data in
1236
+ // incompatible ways, which can lead to an endless cycle of cache
1237
+ // broadcasts and useless network requests. As with any
1238
+ // feud, eventually one side must step back from the brink,
1239
+ // letting the other side(s) have the last word(s). There may
1240
+ // be other points where we could break this cycle, such as
1241
+ // silencing the broadcast for cache.writeQuery (not a good
1242
+ // idea, since it just delays the feud a bit) or somehow
1243
+ // avoiding the network request that just happened (also bad,
1244
+ // because the server could return useful new data). All
1245
+ // options considered, returning early and stopping the
1246
+ // reobserveCacheFirst cycle seems to be the least damaging place to
1247
+ // break the cycle because it allows read functions/custom scalars to
1248
+ // be applied to the feuding query while avoiding the endless cycle of
1249
+ // requests.
1250
+ if (environment_1.__DEV__ && !this.didWarnOnFeud) {
1251
+ this.didWarnOnFeud = true;
1252
+ warnOnFeud(this.query, diff);
1181
1253
  }
1254
+ return;
1182
1255
  }
1256
+ //If this diff did not come from an optimistic transaction
1257
+ // make the ObservableQuery "reobserve" the latest data
1258
+ // using a temporary fetch policy of "cache-first", so complete cache
1259
+ // results have a chance to be delivered without triggering additional
1260
+ // network requests, even when options.fetchPolicy is "network-only"
1261
+ // or "cache-and-network". All other fetch policies are preserved by
1262
+ // this method, and are handled by calling oq.reobserve(). If this
1263
+ // reobservation is spurious, distinctUntilChanged still has a
1264
+ // chance to catch it before delivery to ObservableQuery subscribers.
1265
+ this.reobserveCacheFirst();
1266
+ }
1267
+ deliverCacheDiff(diff) {
1268
+ const current = this.getCurrentResult();
1269
+ this.input.next({
1270
+ kind: "N",
1271
+ value: {
1272
+ data: diff.result,
1273
+ dataState: diff.dataState,
1274
+ networkStatus: current.networkStatus,
1275
+ loading: current.loading,
1276
+ error: undefined,
1277
+ partial: !diff.complete,
1278
+ },
1279
+ source: "cache",
1280
+ query: this.query,
1281
+ variables: this.variables,
1282
+ meta: {},
1283
+ });
1183
1284
  }
1184
1285
  activeOperations = new Set();
1185
1286
  pushOperation(networkStatus) {
@@ -1242,6 +1343,7 @@ class ObservableQuery {
1242
1343
  // exception for cache-only queries - we reset them into a "ready" state
1243
1344
  // as we won't trigger a refetch for them
1244
1345
  const resetToEmpty = this.options.fetchPolicy === "cache-only";
1346
+ this.lastMissing = undefined;
1245
1347
  this.setResult(resetToEmpty ? empty : uninitialized, {
1246
1348
  shouldEmit: resetToEmpty ? 1 /* EmitBehavior.force */ : 2 /* EmitBehavior.never */,
1247
1349
  });
@@ -1386,7 +1488,10 @@ class ObservableQuery {
1386
1488
  reobserveCacheFirst() {
1387
1489
  const { fetchPolicy, nextFetchPolicy } = this.options;
1388
1490
  if (fetchPolicy === "cache-and-network" || fetchPolicy === "network-only") {
1389
- this.reobserve({
1491
+ // Preserve this.lastMissing so a cache update that triggers this
1492
+ // reobserve doesn't reset feud detection. All user-initiated calls to
1493
+ // reobserve (refetch/poll/reobserve, etc) should clear it.
1494
+ this._reobserve({
1390
1495
  fetchPolicy: "cache-first",
1391
1496
  // Use a temporary nextFetchPolicy function that replaces itself with the
1392
1497
  // previous nextFetchPolicy value and returns the original fetchPolicy.
@@ -1402,10 +1507,10 @@ class ObservableQuery {
1402
1507
  // Otherwise go back to the original this.options.fetchPolicy.
1403
1508
  return fetchPolicy;
1404
1509
  },
1405
- });
1510
+ }, { keepLastMissing: true });
1406
1511
  }
1407
1512
  else {
1408
- this.reobserve();
1513
+ this._reobserve(undefined, { keepLastMissing: true });
1409
1514
  }
1410
1515
  }
1411
1516
  getVariablesWithDefaults(variables) {
@@ -1415,7 +1520,7 @@ class ObservableQuery {
1415
1520
  exports.ObservableQuery = ObservableQuery;
1416
1521
  function logMissingFieldErrors(missing) {
1417
1522
  if (environment_1.__DEV__ && missing) {
1418
- __DEV__ && invariant_1.invariant.debug(88, missing);
1523
+ __DEV__ && invariant_1.invariant.debug(89, missing);
1419
1524
  }
1420
1525
  }
1421
1526
  function isEqualQuery(a, b) {
@@ -1458,4 +1563,11 @@ function getTrackingOperatorPromise(defaultValue) {
1458
1563
  });
1459
1564
  return { promise, operator };
1460
1565
  }
1566
+ function warnOnFeud(query, diff) {
1567
+ __DEV__ && invariant_1.invariant.warn(
1568
+ 90,
1569
+ (0, internal_1.getOperationName)(query, "(anonymous)"),
1570
+ diff.missing?.missing
1571
+ );
1572
+ }
1461
1573
  //# sourceMappingURL=ObservableQuery.cjs.map