@apollo/client 4.0.0-alpha.8 → 4.0.0-alpha.9

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 (49) hide show
  1. package/.changeset/forty-tomatoes-punch.md +5 -0
  2. package/.changeset/fuzzy-tips-sit.md +5 -0
  3. package/.changeset/khaki-keys-deliver.md +5 -0
  4. package/.changeset/large-plants-know.md +5 -0
  5. package/.changeset/mean-lizards-think.md +5 -0
  6. package/.changeset/pre.json +6 -0
  7. package/.changeset/thin-peas-hear.md +16 -0
  8. package/CHANGELOG.md +29 -0
  9. package/__cjs/core/ObservableQuery.cjs +91 -28
  10. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  11. package/__cjs/core/ObservableQuery.d.cts +2 -1
  12. package/__cjs/core/QueryManager.cjs +2 -2
  13. package/__cjs/core/QueryManager.cjs.map +1 -1
  14. package/__cjs/core/watchQueryOptions.d.cts +2 -2
  15. package/__cjs/react/hooks/useLazyQuery.cjs +17 -20
  16. package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
  17. package/__cjs/react/hooks/useLazyQuery.d.cts +1 -1
  18. package/__cjs/react/hooks/useMutation.d.cts +1 -1
  19. package/__cjs/react/hooks/useQuery.d.cts +1 -1
  20. package/__cjs/react/query-preloader/createQueryPreloader.cjs +1 -0
  21. package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
  22. package/__cjs/react/types/types.documentation.d.cts +2 -2
  23. package/__cjs/testing/matchers/arrayWithLength.cjs +15 -0
  24. package/__cjs/testing/matchers/arrayWithLength.cjs.map +1 -0
  25. package/__cjs/testing/matchers/arrayWithLength.d.cts +3 -0
  26. package/__cjs/testing/matchers/index.cjs +2 -0
  27. package/__cjs/testing/matchers/index.cjs.map +1 -1
  28. package/__cjs/version.cjs +1 -1
  29. package/core/ObservableQuery.d.ts +2 -1
  30. package/core/ObservableQuery.js +91 -28
  31. package/core/ObservableQuery.js.map +1 -1
  32. package/core/QueryManager.js +2 -2
  33. package/core/QueryManager.js.map +1 -1
  34. package/core/watchQueryOptions.d.ts +2 -2
  35. package/package.json +1 -1
  36. package/react/hooks/useLazyQuery.d.ts +1 -1
  37. package/react/hooks/useLazyQuery.js +17 -20
  38. package/react/hooks/useLazyQuery.js.map +1 -1
  39. package/react/hooks/useMutation.d.ts +1 -1
  40. package/react/hooks/useQuery.d.ts +1 -1
  41. package/react/query-preloader/createQueryPreloader.js +1 -0
  42. package/react/query-preloader/createQueryPreloader.js.map +1 -1
  43. package/react/types/types.documentation.d.ts +2 -2
  44. package/testing/matchers/arrayWithLength.d.ts +3 -0
  45. package/testing/matchers/arrayWithLength.js +11 -0
  46. package/testing/matchers/arrayWithLength.js.map +1 -0
  47. package/testing/matchers/index.js +2 -0
  48. package/testing/matchers/index.js.map +1 -1
  49. package/version.js +1 -1
@@ -9,6 +9,7 @@ import { equalByQuery } from "./equalByQuery.js";
9
9
  import { isNetworkRequestInFlight, NetworkStatus } from "./networkStatus.js";
10
10
  const { assign, hasOwnProperty } = Object;
11
11
  const newNetworkStatusSymbol = Symbol();
12
+ const uninitialized = {};
12
13
  export class ObservableQuery {
13
14
  /**
14
15
  * @internal
@@ -38,7 +39,6 @@ export class ObservableQuery {
38
39
  }
39
40
  subject;
40
41
  observable;
41
- initialResult;
42
42
  isTornDown;
43
43
  queryManager;
44
44
  subscriptions = new Set();
@@ -52,14 +52,8 @@ export class ObservableQuery {
52
52
  networkStatus;
53
53
  constructor({ queryManager, queryInfo, options, }) {
54
54
  this.networkStatus = NetworkStatus.loading;
55
- this.initialResult = {
56
- data: undefined,
57
- loading: true,
58
- networkStatus: this.networkStatus,
59
- partial: true,
60
- };
61
55
  let startedInactive = ObservableQuery.inactiveOnCreation.getValue();
62
- this.subject = new BehaviorSubject(this.initialResult);
56
+ this.subject = new BehaviorSubject(uninitialized);
63
57
  this.observable = this.subject.pipe(tap({
64
58
  subscribe: () => {
65
59
  if (startedInactive) {
@@ -67,6 +61,22 @@ export class ObservableQuery {
67
61
  startedInactive = false;
68
62
  }
69
63
  if (!this.subject.observed) {
64
+ if (this.subject.value === uninitialized) {
65
+ // Emitting a value in the `subscribe` callback of `tap` gives
66
+ // the subject a chance to save this initial result without
67
+ // emitting the placeholder value since this callback is executed
68
+ // before `tap` subscribes to the source observable (the subject).
69
+ // `reobserve` also has the chance to update this value if it
70
+ // synchronously emits one (usually due to reporting a cache
71
+ // value).
72
+ //
73
+ // We don't initialize the `BehaviorSubject` with
74
+ // `getInitialResult` because its possible the cache might have
75
+ // updated between when the `ObservableQuery` was instantiated and
76
+ // when it is subscribed to. Updating the value here ensures we
77
+ // report the most up-to-date result from the cache.
78
+ this.subject.next(this.getInitialResult());
79
+ }
70
80
  this.reobserve();
71
81
  // TODO: See if we can rework updatePolling to better handle this.
72
82
  // reobserve calls updatePolling but this `subscribe` callback is
@@ -81,19 +91,14 @@ export class ObservableQuery {
81
91
  this.tearDownQuery();
82
92
  }
83
93
  },
84
- }),
85
- // TODO: Conditionally filter when notifyOnNetworkStatusChange is true or
86
- // not. We want to emit the loading result if notifyOnNetworkStatusChange
87
- // is true.
88
- filter((result) =>
89
- // TODO: Remove this behavior when unifying loading state for notifyOnNetworkStatusChange
90
- (this.options.fetchPolicy === "no-cache" &&
91
- this.options.notifyOnNetworkStatusChange) ||
92
- // TODO: Remove this behavior when unifying loading state for notifyOnNetworkStatusChange
93
- (this.options.fetchPolicy === "network-only" &&
94
- !this.queryManager.prioritizeCacheValues &&
95
- this.queryInfo.getDiff().complete) ||
96
- result !== this.initialResult));
94
+ }), filter((result) => {
95
+ return (this.options.fetchPolicy !== "standby" &&
96
+ (this.options.notifyOnNetworkStatusChange ||
97
+ !result.loading ||
98
+ // data could be defined for cache-and-network fetch policies
99
+ // when emitting the cache result while loading the network result
100
+ !!result.data));
101
+ }));
97
102
  this["@@observable"] = () => this;
98
103
  if (Symbol.observable) {
99
104
  this[Symbol.observable] = () => this;
@@ -138,6 +143,50 @@ export class ObservableQuery {
138
143
  resetDiff() {
139
144
  this.queryInfo.resetDiff();
140
145
  }
146
+ getInitialResult() {
147
+ const fetchPolicy = this.queryManager.prioritizeCacheValues ?
148
+ "cache-first"
149
+ : this.options.fetchPolicy;
150
+ const defaultResult = {
151
+ data: undefined,
152
+ loading: true,
153
+ networkStatus: NetworkStatus.loading,
154
+ partial: true,
155
+ };
156
+ const cacheResult = () => {
157
+ const diff = this.queryInfo.getDiff();
158
+ return this.maskResult({
159
+ data:
160
+ // TODO: queryInfo.getDiff should handle this since cache.diff returns a
161
+ // null when returnPartialData is false
162
+ this.options.returnPartialData || diff.complete ?
163
+ diff.result ?? undefined
164
+ : undefined,
165
+ loading: !diff.complete,
166
+ networkStatus: diff.complete ? NetworkStatus.ready : NetworkStatus.loading,
167
+ partial: !diff.complete,
168
+ });
169
+ };
170
+ switch (fetchPolicy) {
171
+ case "cache-only":
172
+ case "cache-first":
173
+ return cacheResult();
174
+ case "cache-and-network":
175
+ return {
176
+ ...cacheResult(),
177
+ loading: true,
178
+ networkStatus: NetworkStatus.loading,
179
+ };
180
+ case "standby":
181
+ return {
182
+ ...defaultResult,
183
+ loading: false,
184
+ networkStatus: NetworkStatus.ready,
185
+ };
186
+ default:
187
+ return defaultResult;
188
+ }
189
+ }
141
190
  getCurrentFullResult(saveAsLastResult = true) {
142
191
  // Use the last result as long as the variables match this.variables.
143
192
  const lastResult = this.getLastResult(true);
@@ -313,6 +362,7 @@ export class ObservableQuery {
313
362
  // fetchMore to provide an updateQuery callback that determines how
314
363
  // the data gets written to the cache.
315
364
  fetchPolicy: "no-cache",
365
+ notifyOnNetworkStatusChange: this.options.notifyOnNetworkStatusChange,
316
366
  };
317
367
  combinedOptions.query = this.transformDocument(combinedOptions.query);
318
368
  const qid = this.queryManager.generateQueryId();
@@ -605,10 +655,7 @@ export class ObservableQuery {
605
655
  }
606
656
  const { pollingInfo, options: { pollInterval }, } = this;
607
657
  if (!pollInterval || !this.hasObservers()) {
608
- if (pollingInfo) {
609
- clearTimeout(pollingInfo.timeout);
610
- delete this.pollingInfo;
611
- }
658
+ this.cancelPolling();
612
659
  return;
613
660
  }
614
661
  if (pollingInfo && pollingInfo.interval === pollInterval) {
@@ -646,6 +693,13 @@ export class ObservableQuery {
646
693
  };
647
694
  poll();
648
695
  }
696
+ // This differs from stopPolling in that it does not set pollInterval to 0
697
+ cancelPolling() {
698
+ if (this.pollingInfo) {
699
+ clearTimeout(this.pollingInfo.timeout);
700
+ delete this.pollingInfo;
701
+ }
702
+ }
649
703
  updateLastResult(newResult, variables = this.variables) {
650
704
  let error = this.getLastError();
651
705
  // Preserve this.last.error unless the variables have changed.
@@ -697,6 +751,7 @@ export class ObservableQuery {
697
751
  // We want to ensure we can re-run the custom document transforms the next
698
752
  // time a request is made against the original query.
699
753
  const query = this.transformDocument(options.query);
754
+ const { fetchPolicy } = options;
700
755
  this.lastQuery = query;
701
756
  if (!useDisposableObservable) {
702
757
  // We can skip calling updatePolling if we're not changing this.options.
@@ -707,10 +762,10 @@ export class ObservableQuery {
707
762
  newOptions.variables &&
708
763
  !equal(newOptions.variables, oldVariables) &&
709
764
  // Don't mess with the fetchPolicy if it's currently "standby".
710
- options.fetchPolicy !== "standby" &&
765
+ fetchPolicy !== "standby" &&
711
766
  // If we're changing the fetchPolicy anyway, don't try to change it here
712
767
  // using applyNextFetchPolicy. The explicit options.fetchPolicy wins.
713
- (options.fetchPolicy === oldFetchPolicy ||
768
+ (fetchPolicy === oldFetchPolicy ||
714
769
  // A `nextFetchPolicy` function has even higher priority, though,
715
770
  // so in that case `applyNextFetchPolicy` must be called.
716
771
  typeof options.nextFetchPolicy === "function")) {
@@ -728,6 +783,14 @@ export class ObservableQuery {
728
783
  !equal(newOptions.variables, oldVariables)) {
729
784
  newNetworkStatus = NetworkStatus.setVariables;
730
785
  }
786
+ // QueryManager does not emit any values for standby fetch policies so we
787
+ // want ensure that the networkStatus remains ready.
788
+ if (fetchPolicy === "standby") {
789
+ newNetworkStatus = NetworkStatus.ready;
790
+ }
791
+ }
792
+ if (fetchPolicy === "standby") {
793
+ this.cancelPolling();
731
794
  }
732
795
  this.networkStatus = newNetworkStatus;
733
796
  this.waitForOwnResult &&= skipCacheDataFor(options.fetchPolicy);
@@ -737,7 +800,7 @@ export class ObservableQuery {
737
800
  }
738
801
  };
739
802
  const variables = options.variables && { ...options.variables };
740
- const { notifyOnNetworkStatusChange = false } = options;
803
+ const { notifyOnNetworkStatusChange = true } = options;
741
804
  const { observable, fromLink } = this.fetch(options, newNetworkStatus, notifyOnNetworkStatusChange &&
742
805
  oldNetworkStatus !== newNetworkStatus &&
743
806
  isNetworkRequestInFlight(newNetworkStatus), query);