diff --git a/.api-reports/api-report-core.api.md b/.api-reports/api-report-core.api.md index 815cb7391a..5382952aee 100644 --- a/.api-reports/api-report-core.api.md +++ b/.api-reports/api-report-core.api.md @@ -1866,7 +1866,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -2142,6 +2142,7 @@ export type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -2302,9 +2303,9 @@ interface WriteContext extends ReadMergeModifyContext { // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // src/link/http/selectHttpOptionsAndBody.ts:128:32 - (ae-forgotten-export) The symbol "HttpQueryOptions" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-react.api.md b/.api-reports/api-report-react.api.md index 32dd9f8010..0e32289572 100644 --- a/.api-reports/api-report-react.api.md +++ b/.api-reports/api-report-react.api.md @@ -392,6 +392,7 @@ export interface BaseSubscriptionOptions; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts fetchPolicy?: FetchPolicy; ignoreResults?: boolean; @@ -1647,7 +1648,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1948,6 +1949,7 @@ export interface SubscriptionHookOptions { context?: Context; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -2324,11 +2326,11 @@ interface WatchQueryOptions; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts fetchPolicy?: FetchPolicy; ignoreResults?: boolean; @@ -1461,7 +1462,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1696,6 +1697,7 @@ export interface SubscriptionComponentOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1803,11 +1805,11 @@ interface WatchQueryOptions { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1626,6 +1626,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1724,11 +1725,11 @@ interface WatchQueryOptions { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1632,6 +1632,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1753,11 +1754,11 @@ export function withSubscription; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts fetchPolicy?: FetchPolicy; ignoreResults?: boolean; @@ -1516,7 +1517,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1770,6 +1771,7 @@ interface SubscriptionHookOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -2148,11 +2150,11 @@ interface WatchQueryOptions { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1815,6 +1815,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -2212,11 +2213,11 @@ export function wrapQueryRef(inter // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:174:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts // src/core/types.ts:203:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // src/react/hooks/useBackgroundQuery.ts:38:3 - (ae-forgotten-export) The symbol "SubscribeToMoreFunction" needs to be exported by the entry point index.d.ts // src/react/hooks/useBackgroundQuery.ts:54:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts // src/react/hooks/useBackgroundQuery.ts:78:4 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts diff --git a/.api-reports/api-report-react_ssr.api.md b/.api-reports/api-report-react_ssr.api.md index 6db286b108..c547e86923 100644 --- a/.api-reports/api-report-react_ssr.api.md +++ b/.api-reports/api-report-react_ssr.api.md @@ -1375,7 +1375,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1611,6 +1611,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1709,11 +1710,11 @@ interface WatchQueryOptions { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1661,6 +1661,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1779,11 +1780,11 @@ export function withWarningSpy(it: (...args: TArgs // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:174:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts // src/core/types.ts:203:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-testing_core.api.md b/.api-reports/api-report-testing_core.api.md index aacf32eccc..ddf2c0aa6c 100644 --- a/.api-reports/api-report-testing_core.api.md +++ b/.api-reports/api-report-testing_core.api.md @@ -1413,7 +1413,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1618,6 +1618,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -1736,11 +1737,11 @@ export function withWarningSpy(it: (...args: TArgs // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:174:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts // src/core/types.ts:203:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-utilities.api.md b/.api-reports/api-report-utilities.api.md index 8dc5e2982b..ea225f4f7e 100644 --- a/.api-reports/api-report-utilities.api.md +++ b/.api-reports/api-report-utilities.api.md @@ -2177,7 +2177,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -2478,6 +2478,7 @@ type SubscribeToMoreOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -2667,11 +2668,11 @@ interface WriteContext extends ReadMergeModifyContext { // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:174:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts // src/core/types.ts:203:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // src/utilities/graphql/storeUtils.ts:226:12 - (ae-forgotten-export) The symbol "storeKeyNameStringify" needs to be exported by the entry point index.d.ts // src/utilities/policies/pagination.ts:76:3 - (ae-forgotten-export) The symbol "TRelayEdge" needs to be exported by the entry point index.d.ts // src/utilities/policies/pagination.ts:77:3 - (ae-forgotten-export) The symbol "TRelayPageInfo" needs to be exported by the entry point index.d.ts diff --git a/.api-reports/api-report.api.md b/.api-reports/api-report.api.md index b8b6d18064..58d5f5cbdd 100644 --- a/.api-reports/api-report.api.md +++ b/.api-reports/api-report.api.md @@ -363,6 +363,7 @@ export interface BaseSubscriptionOptions; context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; ignoreResults?: boolean; onComplete?: () => void; @@ -2216,7 +2217,7 @@ class QueryManager { // (undocumented) readonly ssrMode: boolean; // (undocumented) - startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, extensions, }: SubscriptionOptions): Observable>; stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -2580,6 +2581,7 @@ export interface SubscriptionHookOptions { context?: DefaultContext; errorPolicy?: ErrorPolicy; + extensions?: Record; fetchPolicy?: FetchPolicy; query: DocumentNode | TypedDocumentNode; variables?: TVariables; @@ -3014,9 +3016,9 @@ interface WriteContext extends ReadMergeModifyContext { // src/core/ObservableQuery.ts:117:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:124:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts // src/core/QueryManager.ts:158:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:390:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts -// src/core/watchQueryOptions.ts:269:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:391:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "IgnoreModifier" needs to be exported by the entry point index.d.ts +// src/core/watchQueryOptions.ts:272:2 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts // src/link/http/selectHttpOptionsAndBody.ts:128:32 - (ae-forgotten-export) The symbol "HttpQueryOptions" needs to be exported by the entry point index.d.ts // src/react/hooks/useBackgroundQuery.ts:38:3 - (ae-forgotten-export) The symbol "SubscribeToMoreFunction" needs to be exported by the entry point index.d.ts // src/react/hooks/useBackgroundQuery.ts:54:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts diff --git a/.changeset/angry-seals-jog.md b/.changeset/angry-seals-jog.md new file mode 100644 index 0000000000..9ba52ae2f3 --- /dev/null +++ b/.changeset/angry-seals-jog.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": minor +--- + +Support extensions in useSubscription diff --git a/.size-limits.json b/.size-limits.json index c81dd92070..3e400e4779 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 40110, - "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32941 + "dist/apollo-client.min.cjs": 40179, + "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32973 } diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index 92029d9a6f..9d230fcc3c 100644 --- a/src/core/QueryManager.ts +++ b/src/core/QueryManager.ts @@ -293,6 +293,7 @@ export class QueryManager { optimisticResponse: isOptimistic ? optimisticResponse : void 0, }, variables, + {}, false ), @@ -981,52 +982,55 @@ export class QueryManager { errorPolicy = "none", variables, context = {}, + extensions = {}, }: SubscriptionOptions): Observable> { query = this.transform(query); variables = this.getVariables(query, variables); const makeObservable = (variables: OperationVariables) => - this.getObservableFromLink(query, context, variables).map((result) => { - if (fetchPolicy !== "no-cache") { - // the subscription interface should handle not sending us results we no longer subscribe to. - // XXX I don't think we ever send in an object with errors, but we might in the future... - if (shouldWriteResult(result, errorPolicy)) { - this.cache.write({ - query, - result: result.data, - dataId: "ROOT_SUBSCRIPTION", - variables: variables, - }); + this.getObservableFromLink(query, context, variables, extensions).map( + (result) => { + if (fetchPolicy !== "no-cache") { + // the subscription interface should handle not sending us results we no longer subscribe to. + // XXX I don't think we ever send in an object with errors, but we might in the future... + if (shouldWriteResult(result, errorPolicy)) { + this.cache.write({ + query, + result: result.data, + dataId: "ROOT_SUBSCRIPTION", + variables: variables, + }); + } + + this.broadcastQueries(); } - this.broadcastQueries(); - } + const hasErrors = graphQLResultHasError(result); + const hasProtocolErrors = graphQLResultHasProtocolErrors(result); + if (hasErrors || hasProtocolErrors) { + const errors: ApolloErrorOptions = {}; + if (hasErrors) { + errors.graphQLErrors = result.errors; + } + if (hasProtocolErrors) { + errors.protocolErrors = result.extensions[PROTOCOL_ERRORS_SYMBOL]; + } - const hasErrors = graphQLResultHasError(result); - const hasProtocolErrors = graphQLResultHasProtocolErrors(result); - if (hasErrors || hasProtocolErrors) { - const errors: ApolloErrorOptions = {}; - if (hasErrors) { - errors.graphQLErrors = result.errors; - } - if (hasProtocolErrors) { - errors.protocolErrors = result.extensions[PROTOCOL_ERRORS_SYMBOL]; + // `errorPolicy` is a mechanism for handling GraphQL errors, according + // to our documentation, so we throw protocol errors regardless of the + // set error policy. + if (errorPolicy === "none" || hasProtocolErrors) { + throw new ApolloError(errors); + } } - // `errorPolicy` is a mechanism for handling GraphQL errors, according - // to our documentation, so we throw protocol errors regardless of the - // set error policy. - if (errorPolicy === "none" || hasProtocolErrors) { - throw new ApolloError(errors); + if (errorPolicy === "ignore") { + delete result.errors; } - } - if (errorPolicy === "ignore") { - delete result.errors; + return result; } - - return result; - }); + ); if (this.getDocumentInfo(query).hasClientExports) { const observablePromise = this.localState @@ -1088,6 +1092,7 @@ export class QueryManager { query: DocumentNode, context: any, variables?: OperationVariables, + extensions?: Record, // Prefer context.queryDeduplication if specified. deduplication: boolean = context?.queryDeduplication ?? this.queryDeduplication @@ -1106,6 +1111,7 @@ export class QueryManager { ...context, forceFetch: !deduplication, }), + extensions, }; context = operation.context; diff --git a/src/core/watchQueryOptions.ts b/src/core/watchQueryOptions.ts index 5810c6464c..b05cdb13c3 100644 --- a/src/core/watchQueryOptions.ts +++ b/src/core/watchQueryOptions.ts @@ -206,6 +206,9 @@ export interface SubscriptionOptions< /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#context:member} */ context?: DefaultContext; + + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#extensions:member} */ + extensions?: Record; } export interface MutationBaseOptions< diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx index c003585f30..0c9002638d 100644 --- a/src/react/hooks/__tests__/useSubscription.test.tsx +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -459,6 +459,74 @@ describe("useSubscription Hook", () => { expect(context!).toBe("Audi"); }); + it("should share extensions set in options", async () => { + const subscription = gql` + subscription { + car { + make + } + } + `; + + const results = ["Audi", "BMW"].map((make) => ({ + result: { data: { car: { make } } }, + })); + + let extensions: string; + const link = new MockSubscriptionLink(); + const extensionsLink = new ApolloLink((operation, forward) => { + extensions = operation.extensions.make; + return forward(operation); + }); + const client = new ApolloClient({ + link: concat(extensionsLink, link), + cache: new Cache({ addTypename: false }), + }); + + const { result } = renderHook( + () => + useSubscription(subscription, { + extensions: { make: "Audi" }, + }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(result.current.loading).toBe(true); + expect(result.current.error).toBe(undefined); + expect(result.current.data).toBe(undefined); + setTimeout(() => { + link.simulateResult(results[0]); + }, 100); + + await waitFor( + () => { + expect(result.current.data).toEqual(results[0].result.data); + }, + { interval: 1 } + ); + expect(result.current.loading).toBe(false); + expect(result.current.error).toBe(undefined); + + setTimeout(() => { + link.simulateResult(results[1]); + }); + + await waitFor( + () => { + expect(result.current.data).toEqual(results[1].result.data); + }, + { interval: 1 } + ); + expect(result.current.loading).toBe(false); + expect(result.current.error).toBe(undefined); + + expect(extensions!).toBe("Audi"); + }); + it("should handle multiple subscriptions properly", async () => { const subscription = gql` subscription { diff --git a/src/react/hooks/useSubscription.ts b/src/react/hooks/useSubscription.ts index 0bbcb9cf17..fc2280c7bf 100644 --- a/src/react/hooks/useSubscription.ts +++ b/src/react/hooks/useSubscription.ts @@ -146,23 +146,11 @@ export function useSubscription< errorPolicy, shouldResubscribe, context, + extensions, ignoreResults, } = options; const variables = useDeepMemo(() => options.variables, [options.variables]); - let [observable, setObservable] = React.useState(() => - options.skip ? null : ( - createSubscription( - client, - subscription, - variables, - fetchPolicy, - errorPolicy, - context - ) - ) - ); - const recreate = () => createSubscription( client, @@ -170,9 +158,14 @@ export function useSubscription< variables, fetchPolicy, errorPolicy, - context + context, + extensions ); + let [observable, setObservable] = React.useState( + options.skip ? null : recreate + ); + const recreateRef = React.useRef(recreate); useIsomorphicLayoutEffect(() => { recreateRef.current = recreate; @@ -331,17 +324,23 @@ function createSubscription< >( client: ApolloClient, query: TypedDocumentNode, - variables?: TVariables, - fetchPolicy?: FetchPolicy, - errorPolicy?: ErrorPolicy, - context?: DefaultContext + variables: TVariables | undefined, + fetchPolicy: FetchPolicy | undefined, + errorPolicy: ErrorPolicy | undefined, + context: DefaultContext | undefined, + extensions: Record | undefined ) { - const __ = { - variables, - client, + const options = { query, + variables, fetchPolicy, errorPolicy, + context, + extensions, + }; + const __ = { + ...options, + client, result: { loading: true, data: void 0, @@ -359,13 +358,7 @@ function createSubscription< // lazily start the subscription when the first observer subscribes // to get around strict mode if (!observable) { - observable = client.subscribe({ - query, - variables, - fetchPolicy, - errorPolicy, - context, - }); + observable = client.subscribe(options); } const sub = observable.subscribe(observer); return () => sub.unsubscribe(); diff --git a/src/react/types/types.documentation.ts b/src/react/types/types.documentation.ts index c5f232c1b1..515834e7cb 100644 --- a/src/react/types/types.documentation.ts +++ b/src/react/types/types.documentation.ts @@ -552,6 +552,11 @@ export interface SubscriptionOptionsDocumentation { */ context: unknown; + /** + * Shared context between your component and your network interface (Apollo Link). + */ + extensions: unknown; + /** * Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component completes the subscription. * diff --git a/src/react/types/types.ts b/src/react/types/types.ts index ee44164073..cd2e6df1cc 100644 --- a/src/react/types/types.ts +++ b/src/react/types/types.ts @@ -449,6 +449,8 @@ export interface BaseSubscriptionOptions< skip?: boolean; /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#context:member} */ context?: DefaultContext; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#extensions:member} */ + extensions?: Record; /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onComplete:member} */ onComplete?: () => void; /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onData:member} */