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

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 (40) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/__cjs/core/ApolloClient.cjs +3 -1
  3. package/__cjs/core/ApolloClient.cjs.map +1 -1
  4. package/__cjs/core/ApolloClient.d.cts +2 -2
  5. package/__cjs/core/QueryManager.cjs +126 -99
  6. package/__cjs/core/QueryManager.cjs.map +1 -1
  7. package/__cjs/core/QueryManager.d.cts +5 -2
  8. package/__cjs/core/index.cjs.map +1 -1
  9. package/__cjs/core/index.d.cts +1 -1
  10. package/__cjs/core/types.d.cts +21 -1
  11. package/__cjs/invariantErrorCodes.cjs +1 -1
  12. package/__cjs/react/hooks/useSubscription.cjs +11 -16
  13. package/__cjs/react/hooks/useSubscription.cjs.map +1 -1
  14. package/__cjs/utilities/internal/index.cjs +2 -4
  15. package/__cjs/utilities/internal/index.cjs.map +1 -1
  16. package/__cjs/utilities/internal/index.d.cts +0 -1
  17. package/__cjs/version.cjs +1 -1
  18. package/core/ApolloClient.d.ts +2 -2
  19. package/core/ApolloClient.js +3 -1
  20. package/core/ApolloClient.js.map +1 -1
  21. package/core/QueryManager.d.ts +5 -2
  22. package/core/QueryManager.js +111 -80
  23. package/core/QueryManager.js.map +1 -1
  24. package/core/index.d.ts +1 -1
  25. package/core/index.js.map +1 -1
  26. package/core/types.d.ts +21 -1
  27. package/invariantErrorCodes.js +1 -1
  28. package/package.json +1 -1
  29. package/react/hooks/useSubscription.js +11 -16
  30. package/react/hooks/useSubscription.js.map +1 -1
  31. package/utilities/internal/index.d.ts +0 -1
  32. package/utilities/internal/index.js +0 -1
  33. package/utilities/internal/index.js.map +1 -1
  34. package/version.js +1 -1
  35. package/__cjs/utilities/internal/onAnyEvent.cjs +0 -33
  36. package/__cjs/utilities/internal/onAnyEvent.cjs.map +0 -1
  37. package/__cjs/utilities/internal/onAnyEvent.d.cts +0 -18
  38. package/utilities/internal/onAnyEvent.d.ts +0 -18
  39. package/utilities/internal/onAnyEvent.js +0 -30
  40. package/utilities/internal/onAnyEvent.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import { Trie } from "@wry/trie";
2
2
  import { BREAK, Kind, OperationTypeNode, visit } from "graphql";
3
- import { catchError, concat, EMPTY, filter, from, lastValueFrom, map, materialize, mergeMap, mergeWith, Observable, of, share, shareReplay, Subject, tap, } from "rxjs";
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
5
  import { canonicalStringify } from "@apollo/client/cache";
5
6
  import { CombinedGraphQLErrors, graphQLResultHasProtocolErrors, registerLinkError, toErrorLike, } from "@apollo/client/errors";
6
7
  import { PROTOCOL_ERRORS_SYMBOL } from "@apollo/client/errors";
@@ -8,7 +9,7 @@ import { execute } from "@apollo/client/link";
8
9
  import { maskFragment, maskOperation } from "@apollo/client/masking";
9
10
  import { cacheSizes, DocumentTransform, print } from "@apollo/client/utilities";
10
11
  import { __DEV__ } from "@apollo/client/utilities/environment";
11
- import { AutoCleanedWeakCache, checkDocument, filterMap, getDefaultValues, getGraphQLErrorsFromResult, getOperationDefinition, getOperationName, graphQLResultHasError, hasDirectives, hasForcedResolvers, isDocumentNode, isExecutionPatchIncrementalResult, isExecutionPatchResult, isNonEmptyArray, isNonNullObject, makeUniqueId, mergeIncrementalData, onAnyEvent, removeDirectivesFromDocument, toQueryResult, } from "@apollo/client/utilities/internal";
12
+ import { AutoCleanedWeakCache, checkDocument, filterMap, getDefaultValues, getGraphQLErrorsFromResult, getOperationDefinition, getOperationName, graphQLResultHasError, hasDirectives, hasForcedResolvers, isDocumentNode, isExecutionPatchIncrementalResult, isExecutionPatchResult, isNonEmptyArray, isNonNullObject, makeUniqueId, mergeIncrementalData, removeDirectivesFromDocument, toQueryResult, } from "@apollo/client/utilities/internal";
12
13
  import { invariant, newInvariantError, } from "@apollo/client/utilities/invariant";
13
14
  import { isNetworkRequestInFlight, NetworkStatus } from "./networkStatus.js";
14
15
  import { logMissingFieldErrors, ObservableQuery } from "./ObservableQuery.js";
@@ -139,7 +140,7 @@ export class QueryManager {
139
140
  ...context,
140
141
  optimisticResponse: isOptimistic ? optimisticResponse : void 0,
141
142
  }, variables, {}, false)
142
- .pipe(validateDidEmitValue(), mergeMap((result) => {
143
+ .observable.pipe(validateDidEmitValue(), mergeMap((result) => {
143
144
  const hasErrors = graphQLResultHasError(result);
144
145
  if (hasErrors && errorPolicy === "none") {
145
146
  throw new CombinedGraphQLErrors(result);
@@ -428,6 +429,7 @@ export class QueryManager {
428
429
  getDocumentInfo(document) {
429
430
  const { transformCache } = this;
430
431
  if (!transformCache.has(document)) {
432
+ const operationDefinition = getOperationDefinition(document);
431
433
  const cacheEntry = {
432
434
  // TODO These three calls (hasClientExports, shouldForceResolvers, and
433
435
  // usesNonreactiveDirective) are performing independent full traversals
@@ -445,7 +447,8 @@ export class QueryManager {
445
447
  { name: "nonreactive" },
446
448
  { name: "unmask" },
447
449
  ], document),
448
- defaultVars: getDefaultValues(getOperationDefinition(document)),
450
+ operationType: operationDefinition?.operation,
451
+ defaultVars: getDefaultValues(operationDefinition),
449
452
  // Transform any mutation or subscription operations to query operations
450
453
  // so we can read/write them from/to the cache.
451
454
  asQuery: {
@@ -649,65 +652,68 @@ export class QueryManager {
649
652
  checkDocument(query, OperationTypeNode.SUBSCRIPTION);
650
653
  query = this.transform(query);
651
654
  variables = this.getVariables(query, variables);
652
- const makeObservable = (variables) => this.getObservableFromLink(query, context, variables, extensions).pipe(map((rawResult) => {
653
- if (fetchPolicy !== "no-cache") {
654
- // the subscription interface should handle not sending us results we no longer subscribe to.
655
- // XXX I don't think we ever send in an object with errors, but we might in the future...
656
- if (shouldWriteResult(rawResult, errorPolicy)) {
657
- this.cache.write({
658
- query,
659
- result: rawResult.data,
660
- dataId: "ROOT_SUBSCRIPTION",
661
- variables: variables,
662
- });
663
- }
664
- this.broadcastQueries();
665
- }
666
- const result = {
667
- data: rawResult.data ?? undefined,
668
- };
669
- if (graphQLResultHasError(rawResult)) {
670
- result.error = new CombinedGraphQLErrors(rawResult);
671
- }
672
- else if (graphQLResultHasProtocolErrors(rawResult)) {
673
- result.error = rawResult.extensions[PROTOCOL_ERRORS_SYMBOL];
674
- // Don't emit protocol errors added by HttpLink
675
- delete rawResult.extensions[PROTOCOL_ERRORS_SYMBOL];
676
- }
677
- if (rawResult.extensions &&
678
- Object.keys(rawResult.extensions).length) {
679
- result.extensions = rawResult.extensions;
680
- }
681
- if (result.error && errorPolicy === "none") {
682
- result.data = undefined;
683
- }
684
- if (errorPolicy === "ignore") {
685
- delete result.error;
686
- }
687
- return result;
688
- }), catchError((error) => {
689
- if (errorPolicy === "ignore") {
690
- return of({ data: undefined });
691
- }
692
- return of({ data: undefined, error });
693
- }), filter((result) => !!(result.data || result.error)));
694
- if (this.getDocumentInfo(query).hasClientExports) {
695
- if (__DEV__) {
696
- invariant(this.localState, 89, getOperationName(query, "(anonymous)"));
697
- }
698
- const observablePromise = this.localState.getExportedVariables({
655
+ let restart;
656
+ if (__DEV__) {
657
+ invariant(
658
+ !this.getDocumentInfo(query).hasClientExports || this.localState,
659
+ 89,
660
+ getOperationName(query, "(anonymous)")
661
+ );
662
+ }
663
+ const observable = (this.getDocumentInfo(query).hasClientExports ?
664
+ from(this.localState.getExportedVariables({
699
665
  client: this.client,
700
666
  document: query,
701
667
  variables,
702
668
  context,
703
- }).then(makeObservable);
704
- return new Observable((observer) => {
705
- let sub = null;
706
- observablePromise.then((observable) => (sub = observable.subscribe(observer)), observer.error);
707
- return () => sub && sub.unsubscribe();
708
- });
709
- }
710
- return makeObservable(variables);
669
+ }))
670
+ : of(variables)).pipe(mergeMap((variables) => {
671
+ const { observable, restart: res } = this.getObservableFromLink(query, context, variables, extensions);
672
+ restart = res;
673
+ return observable.pipe(map((rawResult) => {
674
+ if (fetchPolicy !== "no-cache") {
675
+ // the subscription interface should handle not sending us results we no longer subscribe to.
676
+ // XXX I don't think we ever send in an object with errors, but we might in the future...
677
+ if (shouldWriteResult(rawResult, errorPolicy)) {
678
+ this.cache.write({
679
+ query,
680
+ result: rawResult.data,
681
+ dataId: "ROOT_SUBSCRIPTION",
682
+ variables: variables,
683
+ });
684
+ }
685
+ this.broadcastQueries();
686
+ }
687
+ const result = {
688
+ data: rawResult.data ?? undefined,
689
+ };
690
+ if (graphQLResultHasError(rawResult)) {
691
+ result.error = new CombinedGraphQLErrors(rawResult);
692
+ }
693
+ else if (graphQLResultHasProtocolErrors(rawResult)) {
694
+ result.error = rawResult.extensions[PROTOCOL_ERRORS_SYMBOL];
695
+ // Don't emit protocol errors added by HttpLink
696
+ delete rawResult.extensions[PROTOCOL_ERRORS_SYMBOL];
697
+ }
698
+ if (rawResult.extensions &&
699
+ Object.keys(rawResult.extensions).length) {
700
+ result.extensions = rawResult.extensions;
701
+ }
702
+ if (result.error && errorPolicy === "none") {
703
+ result.data = undefined;
704
+ }
705
+ if (errorPolicy === "ignore") {
706
+ delete result.error;
707
+ }
708
+ return result;
709
+ }), catchError((error) => {
710
+ if (errorPolicy === "ignore") {
711
+ return of({ data: undefined });
712
+ }
713
+ return of({ data: undefined, error });
714
+ }), filter((result) => !!(result.data || result.error)));
715
+ }));
716
+ return Object.assign(observable, { restart: () => restart?.() });
711
717
  }
712
718
  removeQuery(queryId) {
713
719
  // teardown all links
@@ -738,8 +744,8 @@ export class QueryManager {
738
744
  // Prefer context.queryDeduplication if specified.
739
745
  deduplication = context?.queryDeduplication ??
740
746
  this.queryDeduplication) {
741
- let observable;
742
- const { serverQuery, clientQuery } = this.getDocumentInfo(query);
747
+ let entry = {};
748
+ const { serverQuery, clientQuery, operationType } = this.getDocumentInfo(query);
743
749
  const operationName = getOperationName(query);
744
750
  const executeContext = {
745
751
  client: this.client,
@@ -759,29 +765,51 @@ export class QueryManager {
759
765
  extensions,
760
766
  };
761
767
  context = operation.context;
768
+ function withRestart(source) {
769
+ return new Observable((observer) => {
770
+ function subscribe() {
771
+ return source.subscribe({
772
+ next: observer.next.bind(observer),
773
+ complete: observer.complete.bind(observer),
774
+ error: observer.error.bind(observer),
775
+ });
776
+ }
777
+ let subscription = subscribe();
778
+ entry.restart ||= () => {
779
+ subscription.unsubscribe();
780
+ subscription = subscribe();
781
+ };
782
+ return () => {
783
+ subscription.unsubscribe();
784
+ entry.restart = undefined;
785
+ };
786
+ });
787
+ }
762
788
  if (deduplication) {
763
789
  const printedServerQuery = print(serverQuery);
764
790
  const varJson = canonicalStringify(variables);
765
- const entry = inFlightLinkObservables.lookup(printedServerQuery, varJson);
766
- observable = entry.observable;
767
- if (!observable) {
768
- observable = entry.observable = execute(link, operation, executeContext).pipe(onAnyEvent((event) => {
769
- if ((event.type !== "next" ||
770
- !("hasNext" in event.value) ||
771
- !event.value.hasNext) &&
772
- inFlightLinkObservables.peek(printedServerQuery, varJson) ===
773
- entry) {
791
+ entry = inFlightLinkObservables.lookup(printedServerQuery, varJson);
792
+ if (!entry.observable) {
793
+ entry.observable = execute(link, operation, executeContext).pipe(withRestart, finalize(() => {
794
+ if (inFlightLinkObservables.peek(printedServerQuery, varJson) ===
795
+ entry) {
774
796
  inFlightLinkObservables.remove(printedServerQuery, varJson);
775
797
  }
776
- }), shareReplay({ refCount: true }));
798
+ }),
799
+ // We don't want to replay the last emitted value for
800
+ // subscriptions and instead opt to wait to receive updates until
801
+ // the subscription emits new values.
802
+ operationType === OperationTypeNode.SUBSCRIPTION ?
803
+ share()
804
+ : shareReplay({ refCount: true }));
777
805
  }
778
806
  }
779
807
  else {
780
- observable = execute(link, operation, executeContext);
808
+ entry.observable = execute(link, operation, executeContext).pipe(withRestart);
781
809
  }
782
810
  }
783
811
  else {
784
- observable = of({ data: {} });
812
+ entry.observable = of({ data: {} });
785
813
  }
786
814
  if (clientQuery) {
787
815
  if (__DEV__) {
@@ -793,7 +821,7 @@ export class QueryManager {
793
821
  operationName ?? "(anonymous)"
794
822
  );
795
823
  }
796
- observable = observable.pipe(mergeMap((result) => {
824
+ entry.observable = entry.observable.pipe(mergeMap((result) => {
797
825
  return from(this.localState.execute({
798
826
  client: this.client,
799
827
  document: clientQuery,
@@ -803,11 +831,14 @@ export class QueryManager {
803
831
  }));
804
832
  }));
805
833
  }
806
- return observable.pipe(catchError((error) => {
807
- error = toErrorLike(error);
808
- registerLinkError(error);
809
- throw error;
810
- }));
834
+ return {
835
+ restart: () => entry.restart?.(),
836
+ observable: entry.observable.pipe(catchError((error) => {
837
+ error = toErrorLike(error);
838
+ registerLinkError(error);
839
+ throw error;
840
+ })),
841
+ };
811
842
  }
812
843
  getResultsFromLink(queryInfo, cacheWriteBehavior, options) {
813
844
  const requestId = (queryInfo.lastRequestId = this.generateRequestId());
@@ -816,7 +847,7 @@ export class QueryManager {
816
847
  // missing fragment definitions (for example) before sending this document
817
848
  // through the link chain.
818
849
  const linkDocument = this.cache.transformForLink(options.query);
819
- return this.getObservableFromLink(linkDocument, options.context, options.variables).pipe(map((result) => {
850
+ return this.getObservableFromLink(linkDocument, options.context, options.variables).observable.pipe(map((result) => {
820
851
  const graphQLErrors = getGraphQLErrorsFromResult(result);
821
852
  const hasErrors = graphQLErrors.length > 0;
822
853
  // If we interrupted this request by calling getResultsFromLink again