@apollo/client 4.0.0-alpha.19 → 4.0.0-alpha.20

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 (50) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/__cjs/core/ApolloClient.cjs +2 -2
  3. package/__cjs/core/ApolloClient.cjs.map +1 -1
  4. package/__cjs/core/ApolloClient.d.cts +1 -1
  5. package/__cjs/core/ObservableQuery.cjs +51 -45
  6. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  7. package/__cjs/core/ObservableQuery.d.cts +16 -16
  8. package/__cjs/core/QueryInfo.cjs +27 -31
  9. package/__cjs/core/QueryInfo.cjs.map +1 -1
  10. package/__cjs/core/QueryInfo.d.cts +23 -6
  11. package/__cjs/core/QueryManager.cjs +66 -121
  12. package/__cjs/core/QueryManager.cjs.map +1 -1
  13. package/__cjs/core/QueryManager.d.cts +14 -8
  14. package/__cjs/react/hooks/useQuery.cjs +1 -1
  15. package/__cjs/react/hooks/useQuery.cjs.map +1 -1
  16. package/__cjs/react/hooks/useQuery.d.cts +2 -2
  17. package/__cjs/react/internal/cache/FragmentReference.cjs +4 -1
  18. package/__cjs/react/internal/cache/FragmentReference.cjs.map +1 -1
  19. package/__cjs/react/internal/cache/QueryReference.cjs +5 -18
  20. package/__cjs/react/internal/cache/QueryReference.cjs.map +1 -1
  21. package/__cjs/react/internal/cache/QueryReference.d.cts +6 -43
  22. package/__cjs/react/query-preloader/createQueryPreloader.cjs +8 -2
  23. package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
  24. package/__cjs/react/query-preloader/createQueryPreloader.d.cts +38 -0
  25. package/__cjs/version.cjs +1 -1
  26. package/core/ApolloClient.d.ts +1 -1
  27. package/core/ApolloClient.js +2 -2
  28. package/core/ApolloClient.js.map +1 -1
  29. package/core/ObservableQuery.d.ts +16 -16
  30. package/core/ObservableQuery.js +51 -45
  31. package/core/ObservableQuery.js.map +1 -1
  32. package/core/QueryInfo.d.ts +23 -6
  33. package/core/QueryInfo.js +27 -31
  34. package/core/QueryInfo.js.map +1 -1
  35. package/core/QueryManager.d.ts +14 -8
  36. package/core/QueryManager.js +67 -122
  37. package/core/QueryManager.js.map +1 -1
  38. package/package.json +1 -1
  39. package/react/hooks/useQuery.d.ts +2 -2
  40. package/react/hooks/useQuery.js +2 -2
  41. package/react/hooks/useQuery.js.map +1 -1
  42. package/react/internal/cache/FragmentReference.js +4 -1
  43. package/react/internal/cache/FragmentReference.js.map +1 -1
  44. package/react/internal/cache/QueryReference.d.ts +6 -43
  45. package/react/internal/cache/QueryReference.js +5 -18
  46. package/react/internal/cache/QueryReference.js.map +1 -1
  47. package/react/query-preloader/createQueryPreloader.d.ts +38 -0
  48. package/react/query-preloader/createQueryPreloader.js +9 -3
  49. package/react/query-preloader/createQueryPreloader.js.map +1 -1
  50. package/version.js +1 -1
@@ -1,7 +1,7 @@
1
1
  import { Trie } from "@wry/trie";
2
2
  import { BREAK, Kind, OperationTypeNode, visit } from "graphql";
3
3
  import { Observable } from "rxjs";
4
- import { catchError, concat, EMPTY, filter, finalize, from, lastValueFrom, map, materialize, mergeMap, mergeWith, of, share, shareReplay, Subject, tap, } from "rxjs";
4
+ import { catchError, concat, EMPTY, filter, finalize, from, lastValueFrom, map, materialize, mergeMap, of, share, shareReplay, Subject, tap, } from "rxjs";
5
5
  import { canonicalStringify } from "@apollo/client/cache";
6
6
  import { CombinedGraphQLErrors, graphQLResultHasProtocolErrors, registerLinkError, toErrorLike, } from "@apollo/client/errors";
7
7
  import { PROTOCOL_ERRORS_SYMBOL } from "@apollo/client/errors";
@@ -40,9 +40,10 @@ export class QueryManager {
40
40
  prioritizeCacheValues = false;
41
41
  onBroadcast;
42
42
  mutationStore;
43
- // All the queries that the QueryManager is currently managing (not
44
- // including mutations and subscriptions).
45
- queries = new Map();
43
+ /**
44
+ * All ObservableQueries that currently have at least one subscriber.
45
+ */
46
+ obsQueries = new Set();
46
47
  // Maps from queryId strings to Promise rejection functions for
47
48
  // currently active queries and fetches.
48
49
  // Use protected instead of private field so
@@ -87,9 +88,7 @@ export class QueryManager {
87
88
  * to dispose of this QueryManager instance.
88
89
  */
89
90
  stop() {
90
- this.queries.forEach((_info, queryId) => {
91
- this.removeQuery(queryId);
92
- });
91
+ this.obsQueries.forEach((oq) => oq.stop());
93
92
  this.cancelPendingFetches(newInvariantError(82));
94
93
  }
95
94
  cancelPendingFetches(error) {
@@ -136,6 +135,7 @@ export class QueryManager {
136
135
  });
137
136
  this.broadcastQueries();
138
137
  return new Promise((resolve, reject) => {
138
+ const cause = {};
139
139
  return this.getObservableFromLink(mutation, {
140
140
  ...context,
141
141
  optimisticResponse: isOptimistic ? optimisticResponse : void 0,
@@ -187,7 +187,7 @@ export class QueryManager {
187
187
  document: mutation,
188
188
  data: storeResult.data,
189
189
  fetchPolicy,
190
- id: mutationId,
190
+ cause,
191
191
  }),
192
192
  };
193
193
  if (graphQLResultHasError(storeResult)) {
@@ -262,20 +262,15 @@ export class QueryManager {
262
262
  }
263
263
  const { updateQueries } = mutation;
264
264
  if (updateQueries) {
265
- this.queries.forEach(({ observableQuery }, queryId) => {
265
+ this.obsQueries.forEach((observableQuery) => {
266
266
  const queryName = observableQuery && observableQuery.queryName;
267
267
  if (!queryName || !hasOwnProperty.call(updateQueries, queryName)) {
268
268
  return;
269
269
  }
270
270
  const updater = updateQueries[queryName];
271
- const { document, variables } = this.queries.get(queryId);
271
+ const { query: document, variables } = observableQuery;
272
272
  // Read the current query result from the store.
273
- const { result: currentQueryResult, complete } = cache.diff({
274
- query: document,
275
- variables,
276
- returnPartialData: true,
277
- optimistic: false,
278
- });
273
+ const { result: currentQueryResult, complete } = observableQuery.getCacheDiff({ optimistic: false });
279
274
  if (complete && currentQueryResult) {
280
275
  // Run our reducer using the current query result and the mutation result.
281
276
  const nextQueryResult = updater(currentQueryResult, {
@@ -403,8 +398,11 @@ export class QueryManager {
403
398
  }, mutation.mutationId);
404
399
  return true;
405
400
  }
406
- fetchQuery(queryId, options, networkStatus) {
407
- return lastValueFrom(this.fetchObservableWithInfo(this.getOrCreateQuery(queryId), options, {
401
+ fetchQuery(options, networkStatus) {
402
+ const queryInfo = new QueryInfo({
403
+ queryManager: this,
404
+ });
405
+ return lastValueFrom(this.fetchObservableWithInfo(queryInfo, options, {
408
406
  networkStatus,
409
407
  }).observable.pipe(filterMap((value) => {
410
408
  switch (value.kind) {
@@ -487,38 +485,26 @@ export class QueryManager {
487
485
  if (typeof options.notifyOnNetworkStatusChange === "undefined") {
488
486
  options.notifyOnNetworkStatusChange = true;
489
487
  }
490
- const queryInfo = new QueryInfo(this);
491
488
  const observable = new ObservableQuery({
492
489
  queryManager: this,
493
- queryInfo,
494
490
  options,
491
+ transformedQuery: query,
495
492
  });
496
- observable["lastQuery"] = query;
497
- if (!ObservableQuery["inactiveOnCreation"].getValue()) {
498
- this.queries.set(observable.queryId, queryInfo);
499
- }
500
- // We give queryInfo the transformed query to ensure the first cache diff
501
- // uses the transformed query instead of the raw query
502
- queryInfo.init({ document: query, variables: observable.variables });
503
- queryInfo.setObservableQuery(observable);
504
493
  return observable;
505
494
  }
506
- async query(options, queryId = this.generateQueryId()) {
495
+ async query(options) {
507
496
  const query = this.transform(options.query);
508
- return this.fetchQuery(queryId, {
497
+ return this.fetchQuery({
509
498
  ...options,
510
499
  query,
511
- })
512
- .then((value) => ({
500
+ }).then((value) => ({
513
501
  ...value,
514
502
  data: this.maskOperation({
515
503
  document: query,
516
504
  data: value?.data,
517
505
  fetchPolicy: options.fetchPolicy,
518
- id: queryId,
519
506
  }),
520
- }))
521
- .finally(() => this.removeQuery(queryId));
507
+ }));
522
508
  }
523
509
  queryIdCounter = 1;
524
510
  generateQueryId() {
@@ -541,10 +527,10 @@ export class QueryManager {
541
527
  // store. So, we cancel the promises and observers that we have issued
542
528
  // so far and not yet resolved (in the case of queries).
543
529
  this.cancelPendingFetches(newInvariantError(86));
544
- this.queries.forEach(({ observableQuery }) => {
530
+ this.obsQueries.forEach((observableQuery) => {
545
531
  // Set loading to true so listeners don't trigger unless they want
546
532
  // results with partial data.
547
- observableQuery?.reset();
533
+ observableQuery.reset();
548
534
  });
549
535
  if (this.mutationStore) {
550
536
  this.mutationStore = {};
@@ -553,7 +539,7 @@ export class QueryManager {
553
539
  return this.cache.reset(options);
554
540
  }
555
541
  getObservableQueries(include = "active") {
556
- const queries = new Map();
542
+ const queries = new Set();
557
543
  const queryNames = new Map();
558
544
  const queryNamesAndQueryStrings = new Map();
559
545
  const legacyQueryOptions = new Set();
@@ -573,49 +559,36 @@ export class QueryManager {
573
559
  }
574
560
  });
575
561
  }
576
- this.queries.forEach(({ observableQuery: oq, document }, queryId) => {
577
- if (oq) {
578
- if (include === "all") {
579
- queries.set(queryId, oq);
580
- return;
581
- }
582
- const { queryName, options: { fetchPolicy }, } = oq;
583
- if (fetchPolicy === "standby" ||
584
- (include === "active" && !oq.hasObservers())) {
585
- return;
586
- }
587
- if (include === "active" ||
588
- (queryName && queryNamesAndQueryStrings.has(queryName)) ||
589
- (document && queryNamesAndQueryStrings.has(print(document)))) {
590
- queries.set(queryId, oq);
591
- if (queryName)
592
- queryNamesAndQueryStrings.set(queryName, true);
593
- if (document)
594
- queryNamesAndQueryStrings.set(print(document), true);
595
- }
562
+ this.obsQueries.forEach((oq) => {
563
+ const document = print(this.transform(oq.options.query));
564
+ if (include === "all") {
565
+ queries.add(oq);
566
+ return;
567
+ }
568
+ const { queryName, options: { fetchPolicy }, } = oq;
569
+ if (include === "active" && fetchPolicy === "standby") {
570
+ return;
571
+ }
572
+ if (include === "active" ||
573
+ (queryName && queryNamesAndQueryStrings.has(queryName)) ||
574
+ (document && queryNamesAndQueryStrings.has(document))) {
575
+ queries.add(oq);
576
+ if (queryName)
577
+ queryNamesAndQueryStrings.set(queryName, true);
578
+ if (document)
579
+ queryNamesAndQueryStrings.set(document, true);
596
580
  }
597
581
  });
598
582
  if (legacyQueryOptions.size) {
599
583
  legacyQueryOptions.forEach((options) => {
600
- // We will be issuing a fresh network request for this query, so we
601
- // pre-allocate a new query ID here, using a special prefix to enable
602
- // cleaning up these temporary queries later, after fetching.
603
- const queryId = makeUniqueId("legacyOneTimeQuery");
604
- const queryInfo = this.getOrCreateQuery(queryId).init({
605
- document: options.query,
606
- variables: options.variables,
607
- });
608
584
  const oq = new ObservableQuery({
609
585
  queryManager: this,
610
- queryInfo,
611
586
  options: {
612
587
  ...options,
613
588
  fetchPolicy: "network-only",
614
589
  },
615
590
  });
616
- invariant(oq.queryId === queryId);
617
- queryInfo.setObservableQuery(oq);
618
- queries.set(queryId, oq);
591
+ queries.add(oq);
619
592
  });
620
593
  }
621
594
  if (__DEV__ && queryNamesAndQueryStrings.size) {
@@ -635,13 +608,12 @@ export class QueryManager {
635
608
  }
636
609
  refetchObservableQueries(includeStandby = false) {
637
610
  const observableQueryPromises = [];
638
- this.getObservableQueries(includeStandby ? "all" : "active").forEach((observableQuery, queryId) => {
611
+ this.getObservableQueries(includeStandby ? "all" : "active").forEach((observableQuery) => {
639
612
  const { fetchPolicy } = observableQuery.options;
640
613
  if (includeStandby ||
641
614
  (fetchPolicy !== "standby" && fetchPolicy !== "cache-only")) {
642
615
  observableQueryPromises.push(observableQuery.refetch());
643
616
  }
644
- (this.queries.get(queryId) || observableQuery["queryInfo"]).resetDiff();
645
617
  });
646
618
  this.broadcastQueries();
647
619
  return Promise.all(observableQueryPromises);
@@ -715,27 +687,10 @@ export class QueryManager {
715
687
  }));
716
688
  return Object.assign(observable, { restart: () => restart?.() });
717
689
  }
718
- removeQuery(queryId) {
719
- // teardown all links
720
- // Both `QueryManager.fetchRequest` and `QueryManager.query` create separate promises
721
- // that each add their reject functions to fetchCancelFns.
722
- // A query created with `QueryManager.query()` could trigger a `QueryManager.fetchRequest`.
723
- // The same queryId could have two rejection fns for two promises
724
- this.fetchCancelFns.delete(queryId);
725
- if (this.queries.has(queryId)) {
726
- const oq = this.queries.get(queryId).observableQuery;
727
- if (oq) {
728
- oq["resetNotifications"]();
729
- oq["unsubscribeFromCache"]?.();
730
- oq.stopPolling();
731
- }
732
- this.queries.delete(queryId);
733
- }
734
- }
735
690
  broadcastQueries() {
736
691
  if (this.onBroadcast)
737
692
  this.onBroadcast();
738
- this.queries.forEach((info) => info.observableQuery?.notify());
693
+ this.obsQueries.forEach((observableQuery) => observableQuery.notify());
739
694
  }
740
695
  // Use protected instead of private field so
741
696
  // @apollo/experimental-nextjs-app-support can access type info.
@@ -961,10 +916,6 @@ export class QueryManager {
961
916
  // in case concast creation synchronously cancels the request.
962
917
  const cleanupCancelFn = () => {
963
918
  this.fetchCancelFns.delete(queryInfo.queryId);
964
- // We need to call `complete` on the subject here otherwise the merged
965
- // observable will never complete since it waits for all source
966
- // observables to complete before itself completes.
967
- fetchCancelSubject.complete();
968
919
  };
969
920
  this.fetchCancelFns.set(queryInfo.queryId, (error) => {
970
921
  fetchCancelSubject.next({
@@ -972,8 +923,6 @@ export class QueryManager {
972
923
  error,
973
924
  source: "network",
974
925
  });
975
- fetchCancelSubject.complete();
976
- cleanupCancelFn();
977
926
  });
978
927
  const fetchCancelSubject = new Subject();
979
928
  let observable, containsDataFromLink;
@@ -1007,16 +956,22 @@ export class QueryManager {
1007
956
  observable = sourcesWithInfo.observable;
1008
957
  }
1009
958
  return {
1010
- observable: observable.pipe(tap({ error: cleanupCancelFn, complete: cleanupCancelFn }), mergeWith(fetchCancelSubject), share()),
959
+ // Merge `observable` with `fetchCancelSubject`, in a way that completing or
960
+ // erroring either of them will complete the merged obserable.
961
+ observable: new Observable((observer) => {
962
+ observer.add(cleanupCancelFn);
963
+ observable.subscribe(observer);
964
+ fetchCancelSubject.subscribe(observer);
965
+ }).pipe(share()),
1011
966
  fromLink: containsDataFromLink,
1012
967
  };
1013
968
  }
1014
969
  refetchQueries({ updateCache, include, optimistic = false, removeOptimistic = optimistic ? makeUniqueId("refetchQueries") : void 0, onQueryUpdated, }) {
1015
- const includedQueriesById = new Map();
970
+ const includedQueriesByOq = new Map();
1016
971
  if (include) {
1017
- this.getObservableQueries(include).forEach((oq, queryId) => {
972
+ this.getObservableQueries(include).forEach((oq) => {
1018
973
  const current = oq.getCurrentResult();
1019
- includedQueriesById.set(queryId, {
974
+ includedQueriesByOq.set(oq, {
1020
975
  oq,
1021
976
  lastDiff: {
1022
977
  result: current?.data,
@@ -1070,13 +1025,13 @@ export class QueryManager {
1070
1025
  removeOptimistic,
1071
1026
  onWatchUpdated(watch, diff, lastDiff) {
1072
1027
  const oq = watch.watcher;
1073
- if (oq instanceof ObservableQuery) {
1074
- if (onQueryUpdated && !handled.has(oq)) {
1075
- handled.add(oq);
1028
+ if (oq instanceof ObservableQuery && !handled.has(oq)) {
1029
+ handled.add(oq);
1030
+ if (onQueryUpdated) {
1076
1031
  // Since we're about to handle this query now, remove it from
1077
1032
  // includedQueriesById, in case it was added earlier because of
1078
1033
  // options.include.
1079
- includedQueriesById.delete(oq.queryId);
1034
+ includedQueriesByOq.delete(oq);
1080
1035
  let result = onQueryUpdated(oq, diff, lastDiff);
1081
1036
  if (result === true) {
1082
1037
  // The onQueryUpdated function requested the default refetching
@@ -1098,20 +1053,20 @@ export class QueryManager {
1098
1053
  // If we don't have an onQueryUpdated function, and onQueryUpdated
1099
1054
  // was not disabled by passing null, make sure this query is
1100
1055
  // "included" like any other options.include-specified query.
1101
- includedQueriesById.set(oq.queryId, { oq, lastDiff, diff });
1056
+ includedQueriesByOq.set(oq, { oq, lastDiff, diff });
1102
1057
  }
1103
1058
  }
1104
1059
  },
1105
1060
  });
1106
1061
  }
1107
- if (includedQueriesById.size) {
1108
- includedQueriesById.forEach(({ oq, lastDiff, diff }, queryId) => {
1062
+ if (includedQueriesByOq.size) {
1063
+ includedQueriesByOq.forEach(({ oq, lastDiff, diff }) => {
1109
1064
  let result;
1110
1065
  // If onQueryUpdated is provided, we want to use it for all included
1111
1066
  // queries, even the QueryOptions ones.
1112
1067
  if (onQueryUpdated) {
1113
1068
  if (!diff) {
1114
- diff = this.cache.diff(oq["queryInfo"].getDiffOptions());
1069
+ diff = oq.getCacheDiff();
1115
1070
  }
1116
1071
  result = onQueryUpdated(oq, diff, lastDiff);
1117
1072
  }
@@ -1124,9 +1079,6 @@ export class QueryManager {
1124
1079
  if (result !== false) {
1125
1080
  results.set(oq, result);
1126
1081
  }
1127
- if (queryId.indexOf("legacyOneTimeQuery") >= 0) {
1128
- this.removeQuery(queryId);
1129
- }
1130
1082
  });
1131
1083
  }
1132
1084
  if (removeOptimistic) {
@@ -1141,18 +1093,17 @@ export class QueryManager {
1141
1093
  }
1142
1094
  return results;
1143
1095
  }
1144
- noCacheWarningsByQueryId = new Set();
1096
+ noCacheWarningsByCause = new WeakSet();
1145
1097
  maskOperation(options) {
1146
1098
  const { document, data } = options;
1147
1099
  if (__DEV__) {
1148
- const { fetchPolicy, id } = options;
1100
+ const { fetchPolicy, cause = {} } = options;
1149
1101
  const operationType = getOperationDefinition(document)?.operation;
1150
- const operationId = (operationType?.[0] ?? "o") + id;
1151
1102
  if (this.dataMasking &&
1152
1103
  fetchPolicy === "no-cache" &&
1153
1104
  !isFullyUnmaskedOperation(document) &&
1154
- !this.noCacheWarningsByQueryId.has(operationId)) {
1155
- this.noCacheWarningsByQueryId.add(operationId);
1105
+ !this.noCacheWarningsByCause.has(cause)) {
1106
+ this.noCacheWarningsByCause.add(cause);
1156
1107
  __DEV__ && invariant.warn(92, getOperationName(document, `Unnamed ${operationType ?? "operation"}`));
1157
1108
  }
1158
1109
  }
@@ -1289,12 +1240,6 @@ export class QueryManager {
1289
1240
  return { fromLink: false, observable: EMPTY };
1290
1241
  }
1291
1242
  }
1292
- getOrCreateQuery(queryId) {
1293
- if (queryId && !this.queries.has(queryId)) {
1294
- this.queries.set(queryId, new QueryInfo(this, queryId));
1295
- }
1296
- return this.queries.get(queryId);
1297
- }
1298
1243
  }
1299
1244
  function validateDidEmitValue() {
1300
1245
  let didEmitValue = false;