From 3862f9ba9086394c4cf4c2ecd99e8e0f6cf44885 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 2 Nov 2023 11:14:44 +0100 Subject: [PATCH] Add a defaultContext property on ApolloClient (#11275) --- .api-reports/api-report-core.md | 14 +- .api-reports/api-report-react.md | 14 +- .api-reports/api-report-react_components.md | 14 +- .api-reports/api-report-react_context.md | 14 +- .api-reports/api-report-react_hoc.md | 14 +- .api-reports/api-report-react_hooks.md | 14 +- .api-reports/api-report-react_ssr.md | 14 +- .api-reports/api-report-testing.md | 14 +- .api-reports/api-report-testing_core.md | 14 +- .api-reports/api-report-utilities.md | 14 +- .api-reports/api-report.md | 14 +- .changeset/breezy-spiders-tap.md | 38 ++++ .size-limit.cjs | 4 +- src/core/ApolloClient.ts | 7 + src/core/QueryManager.ts | 10 +- src/core/__tests__/QueryManager/index.ts | 217 ++++++++++++++++++++ 16 files changed, 383 insertions(+), 47 deletions(-) create mode 100644 .changeset/breezy-spiders-tap.md diff --git a/.api-reports/api-report-core.md b/.api-reports/api-report-core.md index fd7fcc664d7..5fd2e468bf4 100644 --- a/.api-reports/api-report-core.md +++ b/.api-reports/api-report-core.md @@ -100,6 +100,8 @@ export class ApolloClient implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -169,6 +171,7 @@ export type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1681,7 +1684,7 @@ export type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1692,6 +1695,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1702,6 +1706,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -2192,9 +2198,9 @@ interface WriteContext extends ReadMergeModifyContext { // src/cache/inmemory/types.ts:126:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:112:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:113:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:116:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:149:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:378:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:119:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:153:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:384:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/watchQueryOptions.ts:191:3 - (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 diff --git a/.api-reports/api-report-react.md b/.api-reports/api-report-react.md index 0e1e91c88b5..ff07bab0665 100644 --- a/.api-reports/api-report-react.md +++ b/.api-reports/api-report-react.md @@ -110,6 +110,8 @@ class ApolloClient implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -204,6 +206,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1508,7 +1511,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1519,6 +1522,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1528,6 +1532,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -2208,9 +2214,9 @@ interface WatchQueryOptions implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -203,6 +205,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1304,7 +1307,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1315,6 +1318,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1324,6 +1328,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1740,9 +1746,9 @@ interface WatchQueryOptions implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -204,6 +206,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1214,7 +1217,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1225,6 +1228,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1234,6 +1238,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1638,9 +1644,9 @@ interface WatchQueryOptions implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -203,6 +205,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1282,7 +1285,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1293,6 +1296,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1302,6 +1306,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1682,9 +1688,9 @@ export function withSubscription implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -200,6 +202,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1428,7 +1431,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1439,6 +1442,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1448,6 +1452,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -2099,9 +2105,9 @@ interface WatchQueryOptions implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -205,6 +207,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1201,7 +1204,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1212,6 +1215,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1221,6 +1225,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1625,9 +1631,9 @@ interface WatchQueryOptions implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -201,6 +203,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1285,7 +1288,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1296,6 +1299,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1306,6 +1310,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -1684,9 +1690,9 @@ export function withWarningSpy(it: (...args: TArgs // src/core/LocalState.ts:46:5 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:112:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:113:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:116:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:149:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:378:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:119:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:153:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:384:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:158:3 - (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts // src/core/types.ts:160:3 - (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts // src/core/types.ts:178:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts diff --git a/.api-reports/api-report-testing_core.md b/.api-reports/api-report-testing_core.md index 8f7dd41a516..c4ee5f52cf0 100644 --- a/.api-reports/api-report-testing_core.md +++ b/.api-reports/api-report-testing_core.md @@ -105,6 +105,8 @@ class ApolloClient implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -200,6 +202,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1239,7 +1242,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1250,6 +1253,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1259,6 +1263,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1640,9 +1646,9 @@ export function withWarningSpy(it: (...args: TArgs // src/core/LocalState.ts:46:5 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:112:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:113:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:116:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:149:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:378:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:119:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:153:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:384:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:158:3 - (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts // src/core/types.ts:160:3 - (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts // src/core/types.ts:178:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts diff --git a/.api-reports/api-report-utilities.md b/.api-reports/api-report-utilities.md index b3815818dfa..2ec8f95a895 100644 --- a/.api-reports/api-report-utilities.md +++ b/.api-reports/api-report-utilities.md @@ -119,6 +119,8 @@ class ApolloClient implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -212,6 +214,7 @@ type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -1949,7 +1952,7 @@ type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -1960,6 +1963,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1969,6 +1973,8 @@ class QueryManager { cache: ApolloCache; // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; + // (undocumented) + readonly defaultContext: Partial; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -2519,9 +2525,9 @@ interface WriteContext extends ReadMergeModifyContext { // src/core/LocalState.ts:71:3 - (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:112:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:113:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:116:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:149:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:378:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:119:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:153:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:384:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/types.ts:158:3 - (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts // src/core/types.ts:160:3 - (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts // src/core/types.ts:178:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts diff --git a/.api-reports/api-report.md b/.api-reports/api-report.md index caf740c5e7a..e48ae78fd4e 100644 --- a/.api-reports/api-report.md +++ b/.api-reports/api-report.md @@ -105,6 +105,8 @@ export class ApolloClient implements DataProxy { // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; @@ -174,6 +176,7 @@ export type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -2062,7 +2065,7 @@ export type QueryListener = (queryInfo: QueryInfo) => void; // @public (undocumented) class QueryManager { - constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, }: { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { cache: ApolloCache; link: ApolloLink; defaultOptions?: DefaultOptions; @@ -2073,6 +2076,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -2083,6 +2087,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -2877,9 +2883,9 @@ interface WriteContext extends ReadMergeModifyContext { // src/cache/inmemory/types.ts:126:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:112:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts // src/core/ObservableQuery.ts:113:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:116:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:149:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts -// src/core/QueryManager.ts:378:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:119:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:153:5 - (ae-forgotten-export) The symbol "LocalState" needs to be exported by the entry point index.d.ts +// src/core/QueryManager.ts:384:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts // src/core/watchQueryOptions.ts:191:3 - (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:24:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts diff --git a/.changeset/breezy-spiders-tap.md b/.changeset/breezy-spiders-tap.md new file mode 100644 index 00000000000..a8af04cea0d --- /dev/null +++ b/.changeset/breezy-spiders-tap.md @@ -0,0 +1,38 @@ +--- +"@apollo/client": patch +--- + +Add a `defaultContext` option and property on `ApolloClient`, e.g. for keeping track of changing auth tokens or dependency injection. + +This can be used e.g. in authentication scenarios, where a new token might be +generated outside of the link chain and should passed into the link chain. + +```js +import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client'; +import { setContext } from '@apollo/client/link/context'; + +const httpLink = createHttpLink({ + uri: '/graphql', +}); + +const authLink = setContext((_, { headers, token }) => { + return { + headers: { + ...headers, + authorization: token ? `Bearer ${token}` : "", + } + } +}); + +const client = new ApolloClient({ + link: authLink.concat(httpLink), + cache: new InMemoryCache() +}); + +// somewhere else in your application +function onNewToken(newToken) { + // token can now be changed for future requests without need for a global + // variable, scoped ref or recreating the client + client.defaultContext.token = newToken +} +``` diff --git a/.size-limit.cjs b/.size-limit.cjs index 2664ba16fa5..031272a0c63 100644 --- a/.size-limit.cjs +++ b/.size-limit.cjs @@ -1,7 +1,7 @@ const checks = [ { path: "dist/apollo-client.min.cjs", - limit: "38010", + limit: "38062", }, { path: "dist/main.cjs", @@ -10,7 +10,7 @@ const checks = [ { path: "dist/index.js", import: "{ ApolloClient, InMemoryCache, HttpLink }", - limit: "32070", + limit: "32113", }, ...[ "ApolloProvider", diff --git a/src/core/ApolloClient.ts b/src/core/ApolloClient.ts index 12b9e7e0e0e..c667286c36a 100644 --- a/src/core/ApolloClient.ts +++ b/src/core/ApolloClient.ts @@ -54,6 +54,7 @@ export type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + defaultContext?: Partial; assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; @@ -150,6 +151,7 @@ export class ApolloClient implements DataProxy { __DEV__, queryDeduplication = true, defaultOptions, + defaultContext, assumeImmutableResults = cache.assumeImmutableResults, resolvers, typeDefs, @@ -199,6 +201,7 @@ export class ApolloClient implements DataProxy { cache: this.cache, link: this.link, defaultOptions: this.defaultOptions, + defaultContext, documentTransform, queryDeduplication, ssrMode, @@ -692,4 +695,8 @@ export class ApolloClient implements DataProxy { public setLink(newLink: ApolloLink) { this.link = this.queryManager.link = newLink; } + + public get defaultContext() { + return this.queryManager.defaultContext; + } } diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index 0d7ef342454..f24e7f2947a 100644 --- a/src/core/QueryManager.ts +++ b/src/core/QueryManager.ts @@ -8,6 +8,7 @@ import { equal } from "@wry/equality"; import type { ApolloLink, FetchResult } from "../link/core/index.js"; import { execute } from "../link/core/index.js"; import { + compact, hasDirectives, isExecutionPatchIncrementalResult, isExecutionPatchResult, @@ -62,6 +63,7 @@ import type { InternalRefetchQueriesOptions, InternalRefetchQueriesResult, InternalRefetchQueriesMap, + DefaultContext, } from "./types.js"; import { LocalState } from "./LocalState.js"; @@ -106,6 +108,7 @@ export class QueryManager { public readonly assumeImmutableResults: boolean; public readonly documentTransform: DocumentTransform; public readonly ssrMode: boolean; + public readonly defaultContext: Partial; private queryDeduplication: boolean; private clientAwareness: Record = {}; @@ -137,6 +140,7 @@ export class QueryManager { clientAwareness = {}, localState, assumeImmutableResults = !!cache.assumeImmutableResults, + defaultContext, }: { cache: ApolloCache; link: ApolloLink; @@ -148,6 +152,7 @@ export class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }) { const defaultDocumentTransform = new DocumentTransform( (document) => this.cache.transformDocument(document), @@ -172,6 +177,7 @@ export class QueryManager { // selections and fragments from the fragment registry. .concat(defaultDocumentTransform) : defaultDocumentTransform; + this.defaultContext = defaultContext || Object.create(null); if ((this.onBroadcast = onBroadcast)) { this.mutationStore = Object.create(null); @@ -1154,7 +1160,9 @@ export class QueryManager { return asyncMap( this.getObservableFromLink( linkDocument, - options.context, + // explicitly a shallow merge so any class instances etc. a user might + // put in here will not be merged into each other. + compact(this.defaultContext, options.context), options.variables ), diff --git a/src/core/__tests__/QueryManager/index.ts b/src/core/__tests__/QueryManager/index.ts index e1eeb02893c..b9ec7fef183 100644 --- a/src/core/__tests__/QueryManager/index.ts +++ b/src/core/__tests__/QueryManager/index.ts @@ -6142,4 +6142,221 @@ describe("QueryManager", () => { } ); }); + + describe("defaultContext", () => { + let _: any; // trash variable to throw away values when destructuring + _ = _; // omit "'_' is declared but its value is never read." compiler warning + + it("ApolloClient and QueryManager share a `defaultContext` instance (default empty object)", () => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: ApolloLink.empty(), + }); + + expect(client.defaultContext).toBe(client["queryManager"].defaultContext); + }); + + it("ApolloClient and QueryManager share a `defaultContext` instance (provided option)", () => { + const defaultContext = {}; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: ApolloLink.empty(), + defaultContext, + }); + + expect(client.defaultContext).toBe(defaultContext); + expect(client["queryManager"].defaultContext).toBe(defaultContext); + }); + + it("`defaultContext` cannot be reassigned on the user-facing `ApolloClient`", () => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: ApolloLink.empty(), + }); + + expect(() => { + // @ts-ignore + client.defaultContext = { query: { fetchPolicy: "cache-only" } }; + }).toThrowError(/Cannot set property defaultContext/); + }); + + it("`defaultContext` will be applied to the context of a query", async () => { + let context: any; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + (operation) => + new Observable((observer) => { + ({ cache: _, ...context } = operation.getContext()); + observer.complete(); + }) + ), + defaultContext: { + foo: "bar", + }, + }); + + await client.query({ + query: gql` + query { + foo + } + `, + }); + + expect(context.foo).toBe("bar"); + }); + + it("`ApolloClient.defaultContext` can be modified and changes will show up in future queries", async () => { + let context: any; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + (operation) => + new Observable((observer) => { + ({ cache: _, ...context } = operation.getContext()); + observer.complete(); + }) + ), + defaultContext: { + foo: "bar", + }, + }); + + // one query to "warm up" with an old value to make sure the value + // isn't locked in at the first query or something + await client.query({ + query: gql` + query { + foo + } + `, + }); + + expect(context.foo).toBe("bar"); + + client.defaultContext.foo = "changed"; + + await client.query({ + query: gql` + query { + foo + } + `, + }); + + expect(context.foo).toBe("changed"); + }); + + it("`defaultContext` will be shallowly merged with explicit context", async () => { + let context: any; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + (operation) => + new Observable((observer) => { + ({ cache: _, ...context } = operation.getContext()); + observer.complete(); + }) + ), + defaultContext: { + foo: { bar: "baz" }, + a: { b: "c" }, + }, + }); + + await client.query({ + query: gql` + query { + foo + } + `, + context: { + a: { x: "y" }, + }, + }); + + expect(context).toEqual( + expect.objectContaining({ + foo: { bar: "baz" }, + a: { b: undefined, x: "y" }, + }) + ); + }); + + it("`defaultContext` will be shallowly merged with context from `defaultOptions.query.context", async () => { + let context: any; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + (operation) => + new Observable((observer) => { + ({ cache: _, ...context } = operation.getContext()); + observer.complete(); + }) + ), + defaultContext: { + foo: { bar: "baz" }, + a: { b: "c" }, + }, + defaultOptions: { + query: { + context: { + a: { x: "y" }, + }, + }, + }, + }); + + await client.query({ + query: gql` + query { + foo + } + `, + }); + + expect(context.foo).toStrictEqual({ bar: "baz" }); + expect(context.a).toStrictEqual({ x: "y" }); + }); + + it( + "document existing behavior: `defaultOptions.query.context` will be " + + "completely overwritten by, not merged with, explicit context", + async () => { + let context: any; + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + (operation) => + new Observable((observer) => { + ({ cache: _, ...context } = operation.getContext()); + observer.complete(); + }) + ), + defaultOptions: { + query: { + context: { + foo: { bar: "baz" }, + }, + }, + }, + }); + + await client.query({ + query: gql` + query { + foo + } + `, + context: { + a: { x: "y" }, + }, + }); + + expect(context.a).toStrictEqual({ x: "y" }); + expect(context.foo).toBeUndefined(); + } + ); + }); });