diff --git a/.api-reports/api-report-cache.md b/.api-reports/api-report-cache.md index 34278f0c522..0f075697b78 100644 --- a/.api-reports/api-report-cache.md +++ b/.api-reports/api-report-cache.md @@ -8,9 +8,10 @@ import type { DocumentNode } from 'graphql'; import type { FieldNode } from 'graphql'; import type { FragmentDefinitionNode } from 'graphql'; import type { InlineFragmentNode } from 'graphql'; +import { Observable } from 'zen-observable-ts'; import type { SelectionSetNode } from 'graphql'; import { Trie } from '@wry/trie'; -import type { TypedDocumentNode } from '@graphql-typed-document-node/core'; +import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "StoreObjectValueMaybeReference" needs to be exported by the entry point index.d.ts // @@ -27,10 +28,13 @@ export abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -49,7 +53,6 @@ export abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -61,6 +64,8 @@ export abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // (undocumented) abstract write(write: Cache_2.WriteOptions): Reference | undefined; // (undocumented) @@ -75,7 +80,7 @@ export type ApolloReducerConfig = { addTypename?: boolean; }; -// @public (undocumented) +// @public type AsStoreObject = { @@ -90,7 +95,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -127,7 +132,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -197,9 +202,9 @@ export const cacheSlot: { withValue(value: ApolloCache, callback: (this: TThis, ...args: TArgs) => TResult, args?: TArgs | undefined, thisArg?: TThis | undefined): TResult; }; -// @public (undocumented) +// @public export const canonicalStringify: ((value: any) => string) & { - reset: typeof resetCanonicalStringify; + reset(): void; }; // @public (undocumented) @@ -219,40 +224,29 @@ export namespace DataProxy { }; // (undocumented) export interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) export interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // (undocumented) export interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) export interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) @@ -266,11 +260,8 @@ export namespace DataProxy { } // (undocumented) export interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // (undocumented) @@ -278,18 +269,48 @@ export namespace DataProxy { } } -// @public (undocumented) +// @public export interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // Warning: (ae-forgotten-export) The symbol "KeyFieldsContext" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -354,7 +375,10 @@ export abstract class EntityStore implements NormalizedCache { has(dataId: string): boolean; // (undocumented) protected lookup(dataId: string, dependOnExistence?: boolean): StoreObject | undefined; - // (undocumented) + makeCacheKey(document: DocumentNode, callback: Cache_2.WatchCallback, details: string): object; + makeCacheKey(selectionSet: SelectionSetNode, parent: string | StoreObject, varString: string | undefined, canonizeResults: boolean): object; + makeCacheKey(field: FieldNode, array: readonly any[], varString: string | undefined): object; + // @deprecated (undocumented) makeCacheKey(...args: any[]): object; // (undocumented) merge(older: string | StoreObject, newer: StoreObject | string): void; @@ -472,7 +496,7 @@ type FieldValueGetter = EntityStore["getFieldValue"]; // @public (undocumented) type FlavorableWriteContext = Pick; -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -488,9 +512,38 @@ export interface FragmentRegistryAPI { // (undocumented) register(...fragments: DocumentNode[]): this; // (undocumented) + resetCaches(): void; + // (undocumented) transform(document: D): D; } +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getInMemoryCacheMemoryInternals: (() => { + addTypenameDocumentTransform: { + cache: number; + }[]; + inMemoryCache: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + }; + fragmentRegistry: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + }; + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + // @public (undocumented) export type IdGetter = (value: IdGetterObj) => string | undefined; @@ -517,8 +570,6 @@ export class InMemoryCache extends ApolloCache { protected broadcastWatches(options?: BroadcastOptions): void; // (undocumented) protected config: InMemoryCacheConfig; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts - // // (undocumented) diff(options: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) @@ -530,6 +581,10 @@ export class InMemoryCache extends ApolloCache { resetResultCache?: boolean; resetResultIdentities?: boolean; }): string[]; + // Warning: (ae-forgotten-export) The symbol "getInMemoryCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getInMemoryCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -562,13 +617,13 @@ export class InMemoryCache extends ApolloCache { // @public (undocumented) export interface InMemoryCacheConfig extends ApolloReducerConfig { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) fragments?: FragmentRegistryAPI; // (undocumented) possibleTypes?: PossibleTypesMap; - // (undocumented) + // @deprecated (undocumented) resultCacheMaxSize?: number; // (undocumented) resultCaching?: boolean; @@ -706,7 +761,7 @@ export type Modifiers = Record> = [FieldName in keyof T]: Modifier>>; }>; -// @public (undocumented) +// @public export interface NormalizedCache { // (undocumented) canRead: CanReadFunction; @@ -732,17 +787,14 @@ export interface NormalizedCache { modify>(dataId: string, fields: Modifiers | AllFieldsModifier): boolean; // (undocumented) release(rootId: string): number; - // (undocumented) replace(newData: NormalizedCacheObject): void; - // (undocumented) retain(rootId: string): number; - // (undocumented) toObject(): NormalizedCacheObject; // (undocumented) toReference: ToReferenceFunction; } -// @public (undocumented) +// @public export interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -807,6 +859,9 @@ export type PossibleTypesMap = { [supertype: string]: string[]; }; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) type ReactiveListener = (value: T) => any; @@ -865,9 +920,6 @@ export interface Reference { readonly __ref: string; } -// @public (undocumented) -function resetCanonicalStringify(): void; - // @public (undocumented) type SafeReadonly = T extends object ? Readonly : T; @@ -885,7 +937,7 @@ export interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) export type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -894,7 +946,7 @@ export type StoreValue = number | string | string[] | Reference | Reference[] | class Stump extends Layer { constructor(root: EntityStore.Root); // (undocumented) - merge(): any; + merge(older: string | StoreObject, newer: string | StoreObject): void; // (undocumented) removeLayer(): this; } @@ -922,6 +974,28 @@ export type TypePolicy = { }; }; +// @public +export interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +export type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) interface WriteContext extends ReadMergeModifyContext { // (undocumented) @@ -956,11 +1030,10 @@ interface WriteContext extends ReadMergeModifyContext { // Warnings were encountered during analysis: // -// src/cache/inmemory/object-canon.ts:203:32 - (ae-forgotten-export) The symbol "resetCanonicalStringify" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:98:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts -// 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/cache/inmemory/policies.ts:92:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/types.ts:139:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-core.md b/.api-reports/api-report-core.md index bd0acbce3df..de1328393e7 100644 --- a/.api-reports/api-report-core.md +++ b/.api-reports/api-report-core.md @@ -20,7 +20,6 @@ import { InvariantError } from 'ts-invariant'; import { Observable } from 'zen-observable-ts'; import type { Subscription as ObservableSubscription } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import { print as print_3 } from 'graphql'; import { resetCaches } from 'graphql-tag'; import type { SelectionSetNode } from 'graphql'; import { setVerbosity as setLogVerbosity } from 'ts-invariant'; @@ -44,10 +43,13 @@ export abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -66,7 +68,6 @@ export abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -78,6 +79,7 @@ export abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + watchFragment(options: WatchFragmentOptions): Observable>; // (undocumented) abstract write(write: Cache_2.WriteOptions): Reference | undefined; // (undocumented) @@ -86,97 +88,85 @@ export abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public export class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; constructor(options: ApolloClientOptions); // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // (undocumented) link: ApolloLink; - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // (undocumented) readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // (undocumented) + watchFragment(options: WatchFragmentOptions): Observable>; watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -export type ApolloClientOptions = { - uri?: string | UriFunction; +export interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) export class ApolloError extends Error { @@ -235,10 +225,16 @@ export class ApolloLink { static execute(link: ApolloLink, operation: GraphQLRequest): Observable; // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // (undocumented) @@ -256,14 +252,18 @@ export interface ApolloPayloadResult, TExtensions = } // @public (undocumented) -export type ApolloQueryResult = { +export interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} // @public (undocumented) export type ApolloReducerConfig = { @@ -271,7 +271,7 @@ export type ApolloReducerConfig = { addTypename?: boolean; }; -// @public (undocumented) +// @public type AsStoreObject = { @@ -298,7 +298,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -336,7 +336,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -412,7 +412,7 @@ const enum CacheWriteBehavior { type CanReadFunction = (value: StoreValue) => boolean; // @public (undocumented) -export const checkFetcher: (fetcher: WindowOrWorkerGlobalScope["fetch"] | undefined) => void; +export const checkFetcher: (fetcher: typeof fetch | undefined) => void; // @public (undocumented) export type ClientParseError = InvariantError & { @@ -433,7 +433,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -449,7 +449,7 @@ export const concat: typeof ApolloLink.concat; // @public (undocumented) export const createHttpLink: (linkOptions?: HttpOptions) => ApolloLink; -// @public (undocumented) +// @public @deprecated (undocumented) export const createSignalIfSupported: () => { controller: boolean; signal: boolean; @@ -469,40 +469,29 @@ export namespace DataProxy { }; // (undocumented) export interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) export interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // (undocumented) export interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) export interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) @@ -516,11 +505,8 @@ export namespace DataProxy { } // (undocumented) export interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // (undocumented) @@ -528,18 +514,48 @@ export namespace DataProxy { } } -// @public (undocumented) +// @public export interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) export interface DefaultContext extends Record { } @@ -592,14 +608,17 @@ export class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -609,9 +628,7 @@ export type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } @@ -665,7 +682,10 @@ abstract class EntityStore implements NormalizedCache { has(dataId: string): boolean; // (undocumented) protected lookup(dataId: string, dependOnExistence?: boolean): StoreObject | undefined; - // (undocumented) + makeCacheKey(document: DocumentNode, callback: Cache_2.WatchCallback, details: string): object; + makeCacheKey(selectionSet: SelectionSetNode, parent: string | StoreObject, varString: string | undefined, canonizeResults: boolean): object; + makeCacheKey(field: FieldNode, array: readonly any[], varString: string | undefined): object; + // @deprecated (undocumented) makeCacheKey(...args: any[]): object; // (undocumented) merge(older: string | StoreObject, newer: StoreObject | string): void; @@ -715,7 +735,7 @@ namespace EntityStore { } } -// @public (undocumented) +// @public export type ErrorPolicy = "none" | "ignore" | "all"; // @public (undocumented) @@ -781,13 +801,11 @@ export interface FetchMoreOptions export interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public export type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // @public (undocumented) @@ -862,7 +880,7 @@ type FieldValueGetter = EntityStore["getFieldValue"]; // @public (undocumented) type FlavorableWriteContext = Pick; -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -881,6 +899,8 @@ interface FragmentRegistryAPI { // (undocumented) register(...fragments: DocumentNode[]): this; // (undocumented) + resetCaches(): void; + // (undocumented) transform(document: D): D; } @@ -893,6 +913,68 @@ export function fromError(errorValue: any): Observable; // @public (undocumented) export function fromPromise(promise: Promise): Observable; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + +// @internal +const getInMemoryCacheMemoryInternals: (() => { + addTypenameDocumentTransform: { + cache: number; + }[]; + inMemoryCache: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + }; + fragmentRegistry: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + }; + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + export { gql } // @public (undocumented) @@ -929,31 +1011,19 @@ export class HttpLink extends ApolloLink { constructor(options?: HttpOptions); // (undocumented) options: HttpOptions; - // (undocumented) - requester: RequestHandler; } // @public (undocumented) export interface HttpOptions { - // (undocumented) credentials?: string; - // (undocumented) - fetch?: WindowOrWorkerGlobalScope["fetch"]; - // (undocumented) + fetch?: typeof fetch; fetchOptions?: any; - // (undocumented) headers?: Record; - // (undocumented) includeExtensions?: boolean; - // (undocumented) includeUnusedVariables?: boolean; - // (undocumented) preserveHeaderCase?: boolean; - // (undocumented) print?: Printer; - // (undocumented) uri?: string | UriFunction; - // (undocumented) useGETForQueries?: boolean; } @@ -980,6 +1050,15 @@ export interface IdGetterObj extends Object { _id?: string; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) export interface IncrementalPayload { // (undocumented) @@ -1018,6 +1097,10 @@ export class InMemoryCache extends ApolloCache { resetResultCache?: boolean; resetResultIdentities?: boolean; }): string[]; + // Warning: (ae-forgotten-export) The symbol "getInMemoryCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getInMemoryCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -1050,7 +1133,7 @@ export class InMemoryCache extends ApolloCache { // @public (undocumented) export interface InMemoryCacheConfig extends ApolloReducerConfig { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // Warning: (ae-forgotten-export) The symbol "FragmentRegistryAPI" needs to be exported by the entry point index.d.ts // @@ -1058,7 +1141,7 @@ export interface InMemoryCacheConfig extends ApolloReducerConfig { fragments?: FragmentRegistryAPI; // (undocumented) possibleTypes?: PossibleTypesMap; - // (undocumented) + // @deprecated (undocumented) resultCacheMaxSize?: number; // (undocumented) resultCaching?: boolean; @@ -1100,7 +1183,7 @@ const _invalidateModifier: unique symbol; // @public (undocumented) export function isApolloError(err: Error): err is ApolloError; -// @public (undocumented) +// @public export function isNetworkRequestSettled(networkStatus?: NetworkStatus): boolean; // @public (undocumented) @@ -1164,15 +1247,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -1295,40 +1376,26 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; - // (undocumented) update?: MutationUpdaterFunction; - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } // @public (undocumented) -type MutationFetchPolicy = Extract; +export type MutationFetchPolicy = Extract; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +export interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1346,6 +1413,14 @@ export type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1358,7 +1433,7 @@ interface MutationStoreValue { variables: Record; } -// @public (undocumented) +// @public @deprecated (undocumented) export type MutationUpdaterFn = (cache: ApolloCache, mutationResult: FetchResult) => void; @@ -1369,21 +1444,14 @@ export type MutationUpdaterFunction void; -// @public (undocumented) +// @public export enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1405,7 +1473,7 @@ export type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) +// @public export interface NormalizedCache { // (undocumented) canRead: CanReadFunction; @@ -1431,17 +1499,14 @@ export interface NormalizedCache { modify>(dataId: string, fields: Modifiers | AllFieldsModifier): boolean; // (undocumented) release(rootId: string): number; - // (undocumented) replace(newData: NormalizedCacheObject): void; - // (undocumented) retain(rootId: string): number; - // (undocumented) toObject(): NormalizedCacheObject; // (undocumented) toReference: ToReferenceFunction; } -// @public (undocumented) +// @public export interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -1460,7 +1525,6 @@ export class ObservableQuery; }); - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1485,7 +1549,6 @@ export class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1493,6 +1556,8 @@ export class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1505,19 +1570,13 @@ export class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } @@ -1539,7 +1598,10 @@ export interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1555,7 +1617,7 @@ export type OptimisticStoreItem = { }; // @public (undocumented) -type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; +type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; // @public (undocumented) export function parseAndCheckHttpResponse(operations: Operation | Operation[]): (response: Response) => Promise; @@ -1611,7 +1673,12 @@ export type PossibleTypesMap = { }; // @public (undocumented) -const print_2: typeof print_3; +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + +// @public (undocumented) +const print_2: ((ast: ASTNode) => string) & { + reset(): void; +}; // @public (undocumented) interface Printer { @@ -1635,7 +1702,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1657,17 +1724,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1681,7 +1750,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 +1761,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1702,6 +1772,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -1728,7 +1800,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1742,7 +1816,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1782,7 +1856,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1794,27 +1867,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } export { QueryOptions as PureQueryOptions } @@ -1916,7 +1981,7 @@ export interface RefetchQueriesResult extends Promise Observable | null; @@ -1941,7 +2006,7 @@ export interface Resolvers { // // @public (undocumented) export function rewriteURIForGET(chosenURI: string, body: Body_2): { - parseError: any; + parseError: unknown; newURI?: undefined; } | { newURI: string; @@ -1966,7 +2031,7 @@ export function selectHttpOptionsAndBodyInternal(operation: Operation, printer: }; // @public (undocumented) -export const selectURI: (operation: Operation, fallbackURI?: string | ((operation: Operation) => string) | undefined) => any; +export const selectURI: (operation: Operation, fallbackURI?: string | ((operation: Operation) => string)) => any; // @public (undocumented) export const serializeFetchParameter: (p: any, label: string) => string; @@ -1987,6 +2052,26 @@ export type ServerParseError = Error & { export { setLogVerbosity } +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) export interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -2015,7 +2100,7 @@ export interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) export type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -2024,7 +2109,7 @@ export type StoreValue = number | string | string[] | Reference | Reference[] | class Stump extends Layer { constructor(root: EntityStore.Root); // (undocumented) - merge(): any; + merge(older: string | StoreObject, newer: string | StoreObject): void; // (undocumented) removeLayer(): this; } @@ -2040,15 +2125,10 @@ export type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -2133,23 +2213,36 @@ export interface UriFunction { (operation: Operation): string; } +// @public +export interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +export type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) export type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -export interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public +export interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // @public (undocumented) @@ -2186,16 +2279,17 @@ interface WriteContext extends ReadMergeModifyContext { // Warnings were encountered during analysis: // -// src/cache/inmemory/policies.ts:98:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts -// 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/watchQueryOptions.ts:191:3 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:92:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/types.ts:139:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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-dev.md b/.api-reports/api-report-dev.md index 8f5baa9c781..9158589f56c 100644 --- a/.api-reports/api-report-dev.md +++ b/.api-reports/api-report-dev.md @@ -14,17 +14,25 @@ interface ErrorCodes { }; } +// @public +export type ErrorMessageHandler = { + (message: string | number, args: string[]): string | undefined; +}; + // @public (undocumented) export function loadDevMessages(): void; // Warning: (ae-forgotten-export) The symbol "ErrorCodes" needs to be exported by the entry point index.d.ts // -// @public (undocumented) -export function loadErrorMessageHandler(...errorCodes: ErrorCodes[]): ((message: string | number, args: unknown[]) => string | undefined) & ErrorCodes; +// @public +export function loadErrorMessageHandler(...errorCodes: ErrorCodes[]): ErrorMessageHandler & ErrorCodes; // @public (undocumented) export function loadErrorMessages(): void; +// @public +export function setErrorMessageHandler(handler: ErrorMessageHandler): void; + // (No @packageDocumentation comment for this package) ``` diff --git a/.api-reports/api-report-link_batch-http.md b/.api-reports/api-report-link_batch-http.md index 0e39566b61a..89008cc6985 100644 --- a/.api-reports/api-report-link_batch-http.md +++ b/.api-reports/api-report-link_batch-http.md @@ -10,7 +10,6 @@ import type { ExecutionResult } from 'graphql'; import type { GraphQLError } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import { print as print_3 } from 'graphql'; // @public (undocumented) class ApolloLink { @@ -30,12 +29,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -60,7 +65,7 @@ export namespace BatchHttpLink { // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export class BatchHttpLink extends ApolloLink { constructor(fetchParams?: BatchHttpLink.Options); // (undocumented) @@ -71,17 +76,11 @@ export class BatchHttpLink extends ApolloLink { namespace BatchLink { // (undocumented) interface Options { - // (undocumented) batchDebounce?: boolean; // Warning: (ae-forgotten-export) The symbol "BatchHandler" needs to be exported by the entry point index.d.ts - // - // (undocumented) batchHandler?: BatchHandler; - // (undocumented) batchInterval?: number; - // (undocumented) batchKey?: (operation: Operation) => string; - // (undocumented) batchMax?: number; } } @@ -161,29 +160,17 @@ interface GraphQLRequest> { // @public (undocumented) interface HttpOptions { - // (undocumented) credentials?: string; - // (undocumented) - fetch?: WindowOrWorkerGlobalScope["fetch"]; - // (undocumented) + fetch?: typeof fetch; fetchOptions?: any; - // (undocumented) headers?: Record; - // (undocumented) includeExtensions?: boolean; - // (undocumented) includeUnusedVariables?: boolean; - // (undocumented) preserveHeaderCase?: boolean; // Warning: (ae-forgotten-export) The symbol "Printer" needs to be exported by the entry point index.d.ts - // - // (undocumented) print?: Printer; // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) uri?: string | UriFunction; - // (undocumented) useGETForQueries?: boolean; } @@ -217,7 +204,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -226,7 +216,9 @@ interface Operation { type Path = ReadonlyArray; // @public (undocumented) -const print_2: typeof print_3; +const print_2: ((ast: ASTNode) => string) & { + reset(): void; +}; // @public (undocumented) interface Printer { diff --git a/.api-reports/api-report-link_batch.md b/.api-reports/api-report-link_batch.md index ecc43997ee7..6f6464edbbd 100644 --- a/.api-reports/api-report-link_batch.md +++ b/.api-reports/api-report-link_batch.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -59,15 +65,10 @@ export type BatchHandler = (operations: Operation[], forward?: (NextLink | undef export namespace BatchLink { // (undocumented) export interface Options { - // (undocumented) batchDebounce?: boolean; - // (undocumented) batchHandler?: BatchHandler; - // (undocumented) batchInterval?: number; - // (undocumented) batchKey?: (operation: Operation) => string; - // (undocumented) batchMax?: number; } } @@ -177,7 +178,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_context.md b/.api-reports/api-report-link_context.md index bd2790f5381..76a7c4e5344 100644 --- a/.api-reports/api-report-link_context.md +++ b/.api-reports/api-report-link_context.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -143,7 +149,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_core.md b/.api-reports/api-report-link_core.md index e711dfe7fc1..ce472253f3c 100644 --- a/.api-reports/api-report-link_core.md +++ b/.api-reports/api-report-link_core.md @@ -23,10 +23,16 @@ export class ApolloLink { static execute(link: ApolloLink, operation: GraphQLRequest): Observable; // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // (undocumented) @@ -143,7 +149,10 @@ export interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_error.md b/.api-reports/api-report-link_error.md index 71bb22bc33a..245cc7946c9 100644 --- a/.api-reports/api-report-link_error.md +++ b/.api-reports/api-report-link_error.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -48,7 +54,6 @@ class ApolloLink { interface DefaultContext extends Record { } -// (undocumented) export interface ErrorHandler { // (undocumented) (error: ErrorResponse): Observable | void; @@ -56,7 +61,6 @@ export interface ErrorHandler { // @public (undocumented) export namespace ErrorLink { - // (undocumented) export interface ErrorHandler { // (undocumented) (error: ErrorResponse): Observable | void; @@ -194,7 +198,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_http.md b/.api-reports/api-report-link_http.md index 09b1e6459dd..6e74d1fd378 100644 --- a/.api-reports/api-report-link_http.md +++ b/.api-reports/api-report-link_http.md @@ -11,7 +11,6 @@ import type { GraphQLError } from 'graphql'; import { InvariantError } from 'ts-invariant'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import { print as print_3 } from 'graphql'; // @public (undocumented) class ApolloLink { @@ -31,12 +30,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -60,7 +65,7 @@ interface Body_2 { } // @public (undocumented) -export const checkFetcher: (fetcher: WindowOrWorkerGlobalScope["fetch"] | undefined) => void; +export const checkFetcher: (fetcher: typeof fetch | undefined) => void; // @public (undocumented) export type ClientParseError = InvariantError & { @@ -72,7 +77,7 @@ export type ClientParseError = InvariantError & { // @public (undocumented) export const createHttpLink: (linkOptions?: HttpOptions) => ApolloLink; -// @public (undocumented) +// @public @deprecated (undocumented) export const createSignalIfSupported: () => { controller: boolean; signal: boolean; @@ -181,31 +186,19 @@ export class HttpLink extends ApolloLink { constructor(options?: HttpOptions); // (undocumented) options: HttpOptions; - // (undocumented) - requester: RequestHandler; } // @public (undocumented) export interface HttpOptions { - // (undocumented) credentials?: string; - // (undocumented) - fetch?: WindowOrWorkerGlobalScope["fetch"]; - // (undocumented) + fetch?: typeof fetch; fetchOptions?: any; - // (undocumented) headers?: Record; - // (undocumented) includeExtensions?: boolean; - // (undocumented) includeUnusedVariables?: boolean; - // (undocumented) preserveHeaderCase?: boolean; - // (undocumented) print?: Printer; - // (undocumented) uri?: string | UriFunction; - // (undocumented) useGETForQueries?: boolean; } @@ -249,7 +242,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -261,7 +257,9 @@ export function parseAndCheckHttpResponse(operations: Operation | Operation[]): type Path = ReadonlyArray; // @public (undocumented) -const print_2: typeof print_3; +const print_2: ((ast: ASTNode) => string) & { + reset(): void; +}; // @public (undocumented) interface Printer { @@ -278,7 +276,7 @@ type RequestHandler = (operation: Operation, forward: NextLink) => Observable string) | undefined) => any; +export const selectURI: (operation: Operation, fallbackURI?: string | ((operation: Operation) => string)) => any; // @public (undocumented) export const serializeFetchParameter: (p: any, label: string) => string; diff --git a/.api-reports/api-report-link_persisted-queries.md b/.api-reports/api-report-link_persisted-queries.md index dda0d3dd1db..7a977f4ce55 100644 --- a/.api-reports/api-report-link_persisted-queries.md +++ b/.api-reports/api-report-link_persisted-queries.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -57,7 +63,17 @@ interface BaseOptions { // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const createPersistedQueryLink: (options: PersistedQueryLink.Options) => ApolloLink; +export const createPersistedQueryLink: (options: PersistedQueryLink.Options) => ApolloLink & { + resetHashCache: () => void; +} & ({ + getMemoryInternals(): { + PersistedQueryLink: { + persistedQueryHashes: number; + }; + }; +} | { + getMemoryInternals?: undefined; +}); // @public (undocumented) interface DefaultContext extends Record { @@ -188,7 +204,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_remove-typename.md b/.api-reports/api-report-link_remove-typename.md index ddec205b0ac..05dcca3dac0 100644 --- a/.api-reports/api-report-link_remove-typename.md +++ b/.api-reports/api-report-link_remove-typename.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -149,7 +155,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -160,7 +169,15 @@ type Path = ReadonlyArray; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function removeTypenameFromVariables(options?: RemoveTypenameFromVariablesOptions): ApolloLink; +export function removeTypenameFromVariables(options?: RemoveTypenameFromVariablesOptions): ApolloLink & ({ + getMemoryInternals(): { + removeTypenameFromVariables: { + getVariableDefinitions: number; + }; + }; +} | { + getMemoryInternals?: undefined; +}); // @public (undocumented) export interface RemoveTypenameFromVariablesOptions { diff --git a/.api-reports/api-report-link_retry.md b/.api-reports/api-report-link_retry.md index a4ce34c4e29..173a281dd67 100644 --- a/.api-reports/api-report-link_retry.md +++ b/.api-reports/api-report-link_retry.md @@ -28,12 +28,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -48,7 +54,7 @@ class ApolloLink { interface DefaultContext extends Record { } -// @public (undocumented) +// @public interface DelayFunction { // (undocumented) (count: number, operation: Operation, error: any): number; @@ -56,11 +62,8 @@ interface DelayFunction { // @public (undocumented) interface DelayFunctionOptions { - // (undocumented) initial?: number; - // (undocumented) jitter?: boolean; - // (undocumented) max?: number; } @@ -156,7 +159,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -167,7 +173,7 @@ type Path = ReadonlyArray; // @public (undocumented) type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; -// @public (undocumented) +// @public interface RetryFunction { // (undocumented) (count: number, operation: Operation, error: any): boolean | Promise; @@ -175,9 +181,7 @@ interface RetryFunction { // @public (undocumented) interface RetryFunctionOptions { - // (undocumented) max?: number; - // (undocumented) retryIf?: (error: any, operation: Operation) => boolean | Promise; } @@ -187,13 +191,9 @@ export namespace RetryLink { export interface Options { // Warning: (ae-forgotten-export) The symbol "RetryFunctionOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RetryFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) attempts?: RetryFunctionOptions | RetryFunction; // Warning: (ae-forgotten-export) The symbol "DelayFunctionOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DelayFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) delay?: DelayFunctionOptions | DelayFunction; } } diff --git a/.api-reports/api-report-link_schema.md b/.api-reports/api-report-link_schema.md index 676e4338cb9..14459f745e9 100644 --- a/.api-reports/api-report-link_schema.md +++ b/.api-reports/api-report-link_schema.md @@ -29,12 +29,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -141,7 +147,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -156,13 +165,9 @@ type RequestHandler = (operation: Operation, forward: NextLink) => Observable unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -152,7 +158,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_utils.md b/.api-reports/api-report-link_utils.md index 061cad690e5..edce7cda76d 100644 --- a/.api-reports/api-report-link_utils.md +++ b/.api-reports/api-report-link_utils.md @@ -55,7 +55,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } diff --git a/.api-reports/api-report-link_ws.md b/.api-reports/api-report-link_ws.md index b746ad6a2db..7969b0cdbc1 100644 --- a/.api-reports/api-report-link_ws.md +++ b/.api-reports/api-report-link_ws.md @@ -30,12 +30,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -142,7 +148,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -163,13 +172,9 @@ interface SingleExecutionResult, TContext = DefaultC // @public (undocumented) export namespace WebSocketLink { - // (undocumented) export interface WebSocketParams { - // (undocumented) options?: ClientOptions; - // (undocumented) uri: string; - // (undocumented) webSocketImpl?: any; } } @@ -183,13 +188,9 @@ export class WebSocketLink extends ApolloLink { request(operation: Operation): Observable | null; } -// (undocumented) export interface WebSocketParams { - // (undocumented) options?: ClientOptions; - // (undocumented) uri: string; - // (undocumented) webSocketImpl?: any; } diff --git a/.api-reports/api-report-react.md b/.api-reports/api-report-react.md index 267adc1a330..dd2e4bee702 100644 --- a/.api-reports/api-report-react.md +++ b/.api-reports/api-report-react.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import type { DocumentNode } from 'graphql'; import type { ExecutionResult } from 'graphql'; @@ -15,10 +13,10 @@ import type { GraphQLError } from 'graphql'; import type { GraphQLErrorExtensions } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import * as React_2 from 'react'; -import { ReactNode } from 'react'; +import type * as ReactTypes from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; import type { VariableDefinitionNode } from 'graphql'; @@ -40,10 +38,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -68,7 +69,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -80,6 +80,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -90,7 +94,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -100,30 +104,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -131,48 +130,28 @@ class ApolloClient implements DataProxy { link: ApolloLink; // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = Context, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -180,50 +159,54 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // Warning: (ae-forgotten-export) The symbol "ApolloConsumerProps" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const ApolloConsumer: React_2.FC; +export const ApolloConsumer: ReactTypes.FC; // @public (undocumented) interface ApolloConsumerProps { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts // // (undocumented) - children: (client: ApolloClient) => React_2.ReactChild | null; + children: (client: ApolloClient) => ReactTypes.ReactNode; } // @public (undocumented) @@ -298,12 +281,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -317,27 +306,34 @@ class ApolloLink { // Warning: (ae-forgotten-export) The symbol "ApolloProviderProps" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const ApolloProvider: React_2.FC>; +export const ApolloProvider: ReactTypes.FC>; // @public (undocumented) interface ApolloProviderProps { // (undocumented) - children: React_2.ReactNode | React_2.ReactNode[] | null; + children: ReactTypes.ReactNode | ReactTypes.ReactNode[] | null; // (undocumented) client: ApolloClient; } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { @@ -355,62 +351,48 @@ export interface BackgroundQueryHookOptions = BackgroundQueryHookOptions, NoInfer>; +type BackgroundQueryHookOptionsNoInfer = BackgroundQueryHookOptions, NoInfer_2>; +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export interface BaseMutationOptions = ApolloCache> extends Omit, "mutation"> { - // (undocumented) +export interface BaseMutationOptions = ApolloCache> extends MutationSharedOptions { client?: ApolloClient; - // (undocumented) ignoreResults?: boolean; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; - // (undocumented) onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; } +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +export interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: Context; - // (undocumented) ssr?: boolean; } // @public (undocumented) export interface BaseSubscriptionOptions { - // (undocumented) client?: ApolloClient; - // (undocumented) context?: Context; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) onComplete?: () => void; - // (undocumented) onData?: (options: OnDataOptions) => any; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) + // @deprecated onSubscriptionComplete?: () => void; - // (undocumented) + // @deprecated onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; - // (undocumented) shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean); - // (undocumented) skip?: boolean; - // (undocumented) variables?: TVariables; } @@ -419,7 +401,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -459,7 +441,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -511,13 +493,6 @@ namespace Cache_2 { import Fragment = DataProxy.Fragment; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -552,7 +527,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -566,6 +541,9 @@ type ConcastSourcesIterable = Iterable>; export interface Context extends Record { } +// @public +export function createQueryPreloader(client: ApolloClient): PreloadQueryFunction; + // @public (undocumented) namespace DataProxy { // (undocumented) @@ -577,44 +555,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -634,11 +601,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -648,15 +612,11 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } @@ -668,7 +628,7 @@ interface DataProxy { // Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends ReadonlyArray ? TItem[] extends T ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; // Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts // @@ -721,14 +681,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -738,9 +701,8 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } @@ -755,7 +717,7 @@ enum DocumentType_2 { } export { DocumentType_2 as DocumentType } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -808,20 +770,15 @@ type FetchMoreFunction = (fetchMor }) => TData; }) => Promise>; -// @public (undocumented) -type FetchMoreOptions = Parameters["fetchMore"]>[0]; - // @public (undocumented) interface FetchMoreQueryOptions { // (undocumented) context?: Context; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -844,7 +801,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -853,8 +810,50 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) -export function getApolloContext(): React_2.Context; +export function getApolloContext(): ReactTypes.Context; // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -883,6 +882,15 @@ export interface IDocumentDefinition { variables: ReadonlyArray; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -899,54 +907,6 @@ interface IncrementalPayload { path: Path; } -// @public (undocumented) -class InternalQueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReferenceOptions" needs to be exported by the entry point index.d.ts - constructor(observable: ObservableQuery, options: InternalQueryReferenceOptions); - // (undocumented) - applyOptions(watchQueryOptions: ObservedOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "ObservedOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) - didChangeOptions(watchQueryOptions: ObservedOptions): boolean; - // Warning: (ae-forgotten-export) The symbol "FetchMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchMore(options: FetchMoreOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts - // - // (undocumented) - readonly key: CacheKey; - // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts - // - // (undocumented) - listen(listener: Listener): () => void; - // (undocumented) - readonly observable: ObservableQuery; - // (undocumented) - promise: Promise>; - // (undocumented) - promiseCache?: Map>>; - // (undocumented) - refetch(variables: OperationVariables | undefined): Promise>; - // (undocumented) - result: ApolloQueryResult; - // (undocumented) - retain(): () => void; - // (undocumented) - get watchQueryOptions(): WatchQueryOptions; -} - -// @public (undocumented) -interface InternalQueryReferenceOptions { - // (undocumented) - autoDisposeTimeoutMs?: number; - // (undocumented) - key: CacheKey; - // (undocumented) - onDispose?: () => void; -} - // Warning: (ae-forgotten-export) The symbol "InternalRefetchQueryDescriptor" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesIncludeShorthand" needs to be exported by the entry point index.d.ts // @@ -1004,32 +964,57 @@ export interface LazyQueryHookExecOptions extends Omit, "skip"> { +export interface LazyQueryHookOptions extends BaseQueryOptions { + // @internal (undocumented) + defaultOptions?: Partial>; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; } -// @public (undocumented) +// @public @deprecated (undocumented) export type LazyQueryResult = QueryResult; // @public (undocumented) -export type LazyQueryResultTuple = [LazyQueryExecFunction, QueryResult]; +export type LazyQueryResultTuple = [ +execute: LazyQueryExecFunction, +result: QueryResult +]; + +// @public (undocumented) +export type LoadableQueryHookFetchPolicy = Extract; + +// @public (undocumented) +export interface LoadableQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: Context; + // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts + errorPolicy?: ErrorPolicy; + fetchPolicy?: LoadableQueryHookFetchPolicy; + queryKey?: string | number | any[]; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; +} +// Warning: (ae-forgotten-export) The symbol "OnlyRequiredProperties" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -type Listener = (promise: Promise>) => void; +export type LoadQueryFunction = (...args: [TVariables] extends [never] ? [] : {} extends OnlyRequiredProperties ? [variables?: TVariables] : [variables: TVariables]) => void; // @public (undocumented) class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -1115,31 +1100,19 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; - // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -1157,7 +1130,6 @@ export type MutationFunction = ApolloCache> extends BaseMutationOptions { - // (undocumented) mutation?: DocumentNode | TypedDocumentNode; } @@ -1165,17 +1137,8 @@ export interface MutationFunctionOptions = ApolloCache> extends BaseMutationOptions { } -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1195,20 +1158,23 @@ type MutationQueryReducersMap { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data?: TData | null; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) reset(): void; } +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1223,8 +1189,8 @@ interface MutationStoreValue { // @public (undocumented) export type MutationTuple = ApolloCache> = [ -(options?: MutationFunctionOptions) => Promise>, -MutationResult +mutate: (options?: MutationFunctionOptions) => Promise>, +result: MutationResult ]; // @public (undocumented) @@ -1233,21 +1199,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1269,8 +1228,9 @@ type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) -export type NoInfer = [T][T extends any ? 0 : never]; +// @public +type NoInfer_2 = [T][T extends any ? 0 : never]; +export { NoInfer_2 as NoInfer } // @public (undocumented) class ObservableQuery extends Observable> { @@ -1279,7 +1239,6 @@ class ObservableQuery; }); - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1304,7 +1263,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1312,6 +1270,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1324,34 +1284,34 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -export type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; - -// @public (undocumented) -const OBSERVED_CHANGED_OPTIONS: readonly ["canonizeResults", "context", "errorPolicy", "fetchPolicy", "refetchWritePolicy", "returnPartialData"]; - -// Warning: (ae-forgotten-export) The symbol "OBSERVED_CHANGED_OPTIONS" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -type ObservedOptions = Pick; +export interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} // @public (undocumented) export interface OnDataOptions { @@ -1361,6 +1321,11 @@ export interface OnDataOptions { data: SubscriptionResult; } +// @public +type OnlyRequiredProperties = { + [K in keyof T as {} extends Pick ? never : K]: T[K]; +}; + // @public (undocumented) type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1383,7 +1348,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: Context) => Context; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: Context) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1397,14 +1365,66 @@ type OperationVariables = Record; // @public (undocumented) export function parser(document: DocumentNode): IDocumentDefinition; +// @public (undocumented) +export namespace parser { + var // (undocumented) + resetCache: () => void; +} + // @public (undocumented) type Path = ReadonlyArray; +// @public +export interface PreloadedQueryRef extends QueryRef { + toPromise(): Promise>; +} + +// @public (undocumented) +export type PreloadQueryFetchPolicy = Extract; + +// @public +export interface PreloadQueryFunction { + // Warning: (ae-forgotten-export) The symbol "PreloadQueryOptionsArg" needs to be exported by the entry point index.d.ts + >(query: DocumentNode | TypedDocumentNode, ...[options]: PreloadQueryOptionsArg, TOptions>): PreloadedQueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables>; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; + }): PreloadedQueryRef | undefined, TVariables>; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + errorPolicy: "ignore" | "all"; + }): PreloadedQueryRef; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + returnPartialData: true; + }): PreloadedQueryRef, TVariables>; + (query: DocumentNode | TypedDocumentNode, ...[options]: PreloadQueryOptionsArg>): PreloadedQueryRef; +} + +// Warning: (ae-forgotten-export) The symbol "VariablesOption" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type PreloadQueryOptions = { + canonizeResults?: boolean; + context?: Context; + errorPolicy?: ErrorPolicy; + fetchPolicy?: PreloadQueryFetchPolicy; + returnPartialData?: boolean; + refetchWritePolicy?: RefetchWritePolicy; +} & VariablesOption; + +// @public (undocumented) +type PreloadQueryOptionsArg = [TVariables] extends [never] ? [ +options?: PreloadQueryOptions & TOptions +] : {} extends OnlyRequiredProperties ? [ +options?: PreloadQueryOptions> & Omit +] : [ +options: PreloadQueryOptions> & Omit +]; + // @public (undocumented) type Primitive = null | undefined | string | number | boolean | symbol | bigint; // @public (undocumented) -const QUERY_REFERENCE_SYMBOL: unique symbol; +const QUERY_REF_BRAND: unique symbol; // @public (undocumented) interface QueryData { @@ -1417,20 +1437,16 @@ interface QueryData { // @public (undocumented) export interface QueryDataOptions extends QueryFunctionOptions { // (undocumented) - children?: (result: QueryResult) => ReactNode; - // (undocumented) + children?: (result: QueryResult) => ReactTypes.ReactNode; query: DocumentNode | TypedDocumentNode; } // @public (undocumented) -export interface QueryFunctionOptions extends BaseQueryOptions { - // (undocumented) +export interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -1452,7 +1468,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1476,17 +1492,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1495,11 +1513,9 @@ class QueryInfo { variables?: Record; } -// @public (undocumented) +// @public @deprecated (undocumented) export interface QueryLazyOptions { - // (undocumented) context?: Context; - // (undocumented) variables?: TVariables; } @@ -1508,7 +1524,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 +1535,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1528,6 +1545,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) @@ -1557,7 +1576,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1571,7 +1592,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1614,7 +1635,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1626,62 +1646,50 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: Context; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } -// @public (undocumented) -export interface QueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; +// @public +export interface QueryRef { + // @internal (undocumented) + [QUERY_REF_BRAND]?(variables: TVariables): TData; +} + +// @public @deprecated (undocumented) +export interface QueryReference extends QueryRef { + // @deprecated (undocumented) + toPromise?: unknown; } // @public (undocumented) export interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } // @public (undocumented) type QueryStoreValue = Pick; -// @public (undocumented) +// @public @deprecated (undocumented) export type QueryTuple = LazyQueryResultTuple; // @public (undocumented) @@ -1774,11 +1782,11 @@ type RefetchWritePolicy = "merge" | "overwrite"; // @public (undocumented) class RenderPromises { // (undocumented) - addObservableQueryPromise(obsQuery: ObservableQuery): ReactNode; + addObservableQueryPromise(obsQuery: ObservableQuery): ReactTypes.ReactNode; // Warning: (ae-forgotten-export) The symbol "QueryData" needs to be exported by the entry point index.d.ts // // (undocumented) - addQueryPromise(queryInstance: QueryData, finish?: () => React.ReactNode): React.ReactNode; + addQueryPromise(queryInstance: QueryData, finish?: () => ReactTypes.ReactNode): ReactTypes.ReactNode; // (undocumented) consumeAndAwaitPromises(): Promise; // (undocumented) @@ -1794,9 +1802,12 @@ class RenderPromises { // @public (undocumented) type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; -// @public (undocumented) +// @public @deprecated (undocumented) export const resetApolloContext: typeof getApolloContext; +// @public (undocumented) +type ResetFunction = () => void; + // @public (undocumented) type Resolver = (rootValue?: any, args?: any, context?: any, info?: { field: FieldNode; @@ -1828,6 +1839,26 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: Context; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = Context, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1859,7 +1890,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1887,7 +1918,7 @@ export interface SubscriptionCurrentObservable { // @public (undocumented) export interface SubscriptionDataOptions extends BaseSubscriptionOptions { // (undocumented) - children?: null | ((result: SubscriptionResult) => JSX.Element | null); + children?: null | ((result: SubscriptionResult) => ReactTypes.ReactNode); // (undocumented) subscription: DocumentNode | TypedDocumentNode; } @@ -1898,27 +1929,19 @@ export interface SubscriptionHookOptions { - // (undocumented) context?: Context; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // @public (undocumented) export interface SubscriptionResult { - // (undocumented) data?: TData; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) + // @internal (undocumented) variables?: TVariables; } @@ -1926,13 +1949,19 @@ export interface SubscriptionResult { export type SuspenseQueryHookFetchPolicy = Extract; // @public (undocumented) -export interface SuspenseQueryHookOptions extends Pick, "client" | "variables" | "errorPolicy" | "context" | "canonizeResults" | "returnPartialData" | "refetchWritePolicy"> { - // (undocumented) +export interface SuspenseQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: Context; + errorPolicy?: ErrorPolicy; fetchPolicy?: SuspenseQueryHookFetchPolicy; - // (undocumented) queryKey?: string | number | any[]; - // (undocumented) + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + // @deprecated skip?: boolean; + variables?: TVariables; } // @public (undocumented) @@ -1992,7 +2021,7 @@ export function useApolloClient(override?: ApolloClient): ApolloClient, "variables">>(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer & TOptions): [ -(QueryReference | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData> | (TOptions["skip"] extends boolean ? undefined : never)), +(QueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables> | (TOptions["skip"] extends boolean ? undefined : never)), UseBackgroundQueryResult ]; @@ -2001,7 +2030,7 @@ export function useBackgroundQuery | undefined>, +QueryRef | undefined, TVariables>, UseBackgroundQueryResult ]; @@ -2009,7 +2038,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { errorPolicy: "ignore" | "all"; }): [ -QueryReference, +QueryRef, UseBackgroundQueryResult ]; @@ -2018,7 +2047,7 @@ export function useBackgroundQuery> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; @@ -2026,7 +2055,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; }): [ -QueryReference>, +QueryRef, TVariables>, UseBackgroundQueryResult ]; @@ -2034,12 +2063,12 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { skip: boolean; }): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; // @public (undocumented) -export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryReference, UseBackgroundQueryResult]; +export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryRef, UseBackgroundQueryResult]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken): [undefined, UseBackgroundQueryResult]; @@ -2048,13 +2077,13 @@ export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; })): [ -QueryReference> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | BackgroundQueryHookOptionsNoInfer): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; @@ -2068,7 +2097,8 @@ export type UseBackgroundQueryResult(options: UseFragmentOptions): UseFragmentResult; // @public (undocumented) -export interface UseFragmentOptions extends Omit, NoInfer>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { +export interface UseFragmentOptions extends Omit, NoInfer_2>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { + client?: ApolloClient; // (undocumented) from: StoreObject | Reference | string; // (undocumented) @@ -2086,76 +2116,115 @@ export type UseFragmentResult = { missing?: MissingTree; }; +// @public +export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer_2>): LazyQueryResultTuple; + // @public (undocumented) -export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer>): LazyQueryResultTuple; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions & TOptions): UseLoadableQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables>; // @public (undocumented) -export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer, TContext, TCache>): MutationTuple; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult | undefined, TVariables>; // @public (undocumented) -export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer>): QueryResult; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult; + +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; +}): UseLoadableQueryResult, TVariables>; + +// @public +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions): UseLoadableQueryResult; + +// @public (undocumented) +export type UseLoadableQueryResult = [ +loadQuery: LoadQueryFunction, +queryRef: QueryRef | null, +handlers: { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; + reset: ResetFunction; +} +]; + +// @public +export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer_2, TContext, TCache>): MutationTuple; + +// @public +export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer_2>): QueryResult; + +// @public +export function useQueryRefHandlers(queryRef: QueryRef): UseQueryRefHandlersResult; + +// @public (undocumented) +export interface UseQueryRefHandlersResult { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; +} // Warning: (ae-forgotten-export) The symbol "ReactiveVar" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export function useReactiveVar(rv: ReactiveVar): T; // @public (undocumented) -export function useReadQuery(queryRef: QueryReference): UseReadQueryResult; +export function useReadQuery(queryRef: QueryRef): UseReadQueryResult; // @public (undocumented) export interface UseReadQueryResult { - // (undocumented) data: TData; - // (undocumented) error: ApolloError | undefined; - // (undocumented) networkStatus: NetworkStatus; } -// @public (undocumented) -export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer>): SubscriptionResult; +// @public +export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): SubscriptionResult; // @public (undocumented) -export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; +export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; returnPartialData: true; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; }): UseSuspenseQueryResult, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; })): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) export interface UseSuspenseQueryResult { @@ -2178,47 +2247,67 @@ export interface UseSuspenseQueryResult = [ +TVariables +] extends [never] ? { + variables?: Record; +} : {} extends OnlyRequiredProperties ? { + variables?: TVariables; +} : { + variables: TVariables; +}; + +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; // @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; + +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/react/hooks/useBackgroundQuery.ts:24:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts -// src/react/hooks/useBackgroundQuery.ts:25:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts -// src/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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/react/hooks/useBackgroundQuery.ts:29:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useBackgroundQuery.ts:30:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useLoadableQuery.ts:107:1 - (ae-forgotten-export) The symbol "ResetFunction" 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_components.md b/.api-reports/api-report-react_components.md index 1c8651938b5..251839f2f28 100644 --- a/.api-reports/api-report-react_components.md +++ b/.api-reports/api-report-react_components.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import type { DocumentNode } from 'graphql'; import type { ExecutionResult } from 'graphql'; @@ -16,8 +14,10 @@ import type { GraphQLErrorExtensions } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; import * as PropTypes from 'prop-types'; +import type * as ReactTypes from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription as Subscription_2 } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -38,10 +38,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -66,7 +69,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -78,6 +80,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -88,7 +94,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -98,30 +104,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -130,48 +131,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -179,38 +160,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -274,12 +259,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -291,77 +282,68 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { [K in keyof T]: T[K]; }; +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseMutationOptions = ApolloCache> extends Omit, "mutation"> { +interface BaseMutationOptions = ApolloCache> extends MutationSharedOptions { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts - // - // (undocumented) client?: ApolloClient; - // (undocumented) ignoreResults?: boolean; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; - // (undocumented) onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; } +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } // @public (undocumented) interface BaseSubscriptionOptions { - // (undocumented) client?: ApolloClient; - // (undocumented) context?: DefaultContext; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) onComplete?: () => void; // Warning: (ae-forgotten-export) The symbol "OnDataOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) onData?: (options: OnDataOptions) => any; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) + // @deprecated onSubscriptionComplete?: () => void; // Warning: (ae-forgotten-export) The symbol "OnSubscriptionDataOptions" needs to be exported by the entry point index.d.ts // - // (undocumented) + // @deprecated onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; - // (undocumented) shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean); - // (undocumented) skip?: boolean; - // (undocumented) variables?: TVariables; } @@ -370,7 +352,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -410,7 +392,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -491,7 +473,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -512,44 +494,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -569,11 +540,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -583,18 +551,48 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -626,14 +624,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -643,13 +644,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -696,13 +696,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -725,7 +723,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -734,6 +732,48 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -751,6 +791,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -819,15 +868,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -911,8 +958,8 @@ type Modifiers = Record> = Partia [FieldName in keyof T]: Modifier>>; }>; -// @public (undocumented) -export function Mutation(props: MutationComponentOptions): JSX.Element | null; +// @public @deprecated (undocumented) +export function Mutation(props: MutationComponentOptions): ReactTypes.JSX.Element | null; // @public (undocumented) export namespace Mutation { @@ -928,31 +975,20 @@ export interface Mutation { // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -964,7 +1000,7 @@ export interface MutationComponentOptions, result: MutationResult) => JSX.Element | null; + children: (mutateFunction: MutationFunction, result: MutationResult) => ReactTypes.JSX.Element | null; // (undocumented) mutation: DocumentNode | TypedDocumentNode; } @@ -979,21 +1015,11 @@ type MutationFunction = ApolloCache> extends BaseMutationOptions { - // (undocumented) mutation?: DocumentNode | TypedDocumentNode; } -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1013,20 +1039,23 @@ type MutationQueryReducersMap { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data?: TData | null; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) reset(): void; } +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1045,21 +1074,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1091,8 +1113,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1117,7 +1137,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1125,6 +1144,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1137,26 +1158,34 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; +interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} // @public (undocumented) interface OnDataOptions { @@ -1190,7 +1219,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1202,7 +1234,10 @@ type OperationVariables = Record; type Path = ReadonlyArray; // @public (undocumented) -export function Query(props: QueryComponentOptions): JSX.Element | null; +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + +// @public @deprecated (undocumented) +export function Query(props: QueryComponentOptions): ReactTypes.JSX.Element | null; // @public (undocumented) export namespace Query { @@ -1223,7 +1258,7 @@ export interface QueryComponentOptions) => JSX.Element | null; + children: (result: QueryResult) => ReactTypes.JSX.Element | null; // (undocumented) query: DocumentNode | TypedDocumentNode; } @@ -1231,14 +1266,11 @@ export interface QueryComponentOptions extends BaseQueryOptions { - // (undocumented) +interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -1256,7 +1288,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1280,17 +1312,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1304,7 +1338,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 +1349,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1324,6 +1359,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) @@ -1353,7 +1390,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1367,7 +1406,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1410,7 +1449,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1422,27 +1460,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1450,21 +1480,13 @@ interface QueryOptions { // // @public (undocumented) interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } @@ -1569,6 +1591,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1594,7 +1637,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1608,8 +1651,8 @@ type SubscribeToMoreOptions(props: SubscriptionComponentOptions): JSX.Element | null; +// @public @deprecated (undocumented) +export function Subscription(props: SubscriptionComponentOptions): ReactTypes.JSX.Element | null; // @public (undocumented) export namespace Subscription { @@ -1628,34 +1671,25 @@ export interface Subscription { // @public (undocumented) export interface SubscriptionComponentOptions extends BaseSubscriptionOptions { // (undocumented) - children?: null | ((result: SubscriptionResult) => JSX.Element | null); - // (undocumented) + children?: null | ((result: SubscriptionResult) => ReactTypes.JSX.Element | null); subscription: DocumentNode | TypedDocumentNode; } // @public (undocumented) interface SubscriptionOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // @public (undocumented) interface SubscriptionResult { - // (undocumented) data?: TData; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) + // @internal (undocumented) variables?: TVariables; } @@ -1709,46 +1743,54 @@ interface UriFunction { (operation: Operation): string; } +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-react_context.md b/.api-reports/api-report-react_context.md index 594fb04aeed..f632b81a187 100644 --- a/.api-reports/api-report-react_context.md +++ b/.api-reports/api-report-react_context.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import type { DocumentNode } from 'graphql'; import type { ExecutionResult } from 'graphql'; @@ -15,10 +13,10 @@ import type { GraphQLError } from 'graphql'; import type { GraphQLErrorExtensions } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import * as React_2 from 'react'; -import { ReactNode } from 'react'; +import type * as ReactTypes from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -39,10 +37,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -67,7 +68,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -79,6 +79,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -89,7 +93,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -99,30 +103,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -131,48 +130,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -180,48 +159,52 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) -export const ApolloConsumer: React_2.FC; +export const ApolloConsumer: ReactTypes.FC; // @public (undocumented) export interface ApolloConsumerProps { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts // // (undocumented) - children: (client: ApolloClient) => React_2.ReactChild | null; + children: (client: ApolloClient) => ReactTypes.ReactNode; } // @public (undocumented) @@ -296,12 +279,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -313,40 +302,46 @@ class ApolloLink { } // @public (undocumented) -export const ApolloProvider: React_2.FC>; +export const ApolloProvider: ReactTypes.FC>; // @public (undocumented) export interface ApolloProviderProps { // (undocumented) - children: React_2.ReactNode | React_2.ReactNode[] | null; + children: ReactTypes.ReactNode | ReactTypes.ReactNode[] | null; // (undocumented) client: ApolloClient; } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { [K in keyof T]: T[K]; }; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } @@ -355,7 +350,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -395,7 +390,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -476,7 +471,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -497,44 +492,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -554,11 +538,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -568,18 +549,48 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -611,14 +622,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -628,13 +642,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -681,13 +694,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -710,7 +721,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -719,8 +730,50 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) -export function getApolloContext(): React_2.Context; +export function getApolloContext(): ReactTypes.Context; // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -739,6 +792,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -807,15 +869,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -901,31 +961,20 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -934,17 +983,10 @@ interface MutationBaseOptions; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -962,6 +1004,15 @@ type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -980,21 +1031,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1026,8 +1070,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1052,7 +1094,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1060,6 +1101,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1072,26 +1115,34 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; +interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} // @public (undocumented) type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1107,7 +1158,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1118,6 +1172,9 @@ type OperationVariables = Record; // @public (undocumented) type Path = ReadonlyArray; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) interface QueryData { // (undocumented) @@ -1133,22 +1190,18 @@ interface QueryDataOptions) => ReactNode; - // (undocumented) + children?: (result: QueryResult) => ReactTypes.ReactNode; query: DocumentNode | TypedDocumentNode; } // Warning: (ae-forgotten-export) The symbol "BaseQueryOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface QueryFunctionOptions extends BaseQueryOptions { - // (undocumented) +interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -1166,7 +1219,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1190,17 +1243,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1214,7 +1269,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 +1280,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1234,6 +1290,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) @@ -1263,7 +1321,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1277,7 +1337,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1320,7 +1380,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1332,27 +1391,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1360,21 +1411,13 @@ interface QueryOptions { // // @public (undocumented) interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } @@ -1448,11 +1491,11 @@ type RefetchWritePolicy = "merge" | "overwrite"; // @public (undocumented) class RenderPromises { // (undocumented) - addObservableQueryPromise(obsQuery: ObservableQuery): ReactNode; + addObservableQueryPromise(obsQuery: ObservableQuery): ReactTypes.ReactNode; // Warning: (ae-forgotten-export) The symbol "QueryData" needs to be exported by the entry point index.d.ts // // (undocumented) - addQueryPromise(queryInstance: QueryData, finish?: () => React.ReactNode): React.ReactNode; + addQueryPromise(queryInstance: QueryData, finish?: () => ReactTypes.ReactNode): ReactTypes.ReactNode; // (undocumented) consumeAndAwaitPromises(): Promise; // Warning: (ae-forgotten-export) The symbol "QueryDataOptions" needs to be exported by the entry point index.d.ts @@ -1470,7 +1513,7 @@ class RenderPromises { // @public (undocumented) type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; -// @public (undocumented) +// @public @deprecated (undocumented) export const resetApolloContext: typeof getApolloContext; // @public (undocumented) @@ -1504,6 +1547,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1529,7 +1593,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1545,15 +1609,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -1607,46 +1666,54 @@ interface UriFunction { (operation: Operation): string; } +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-react_hoc.md b/.api-reports/api-report-react_hoc.md index 017659e7ecc..9e7746e9d73 100644 --- a/.api-reports/api-report-react_hoc.md +++ b/.api-reports/api-report-react_hoc.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import type { DocumentNode } from 'graphql'; import type { ExecutionResult } from 'graphql'; @@ -15,9 +13,10 @@ import type { GraphQLError } from 'graphql'; import type { GraphQLErrorExtensions } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import * as React_2 from 'react'; +import type * as ReactTypes from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -38,10 +37,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -66,7 +68,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -78,6 +79,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -88,7 +93,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -98,30 +103,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -130,48 +130,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -179,38 +159,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -274,12 +258,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -291,45 +281,47 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { [K in keyof T]: T[K]; }; +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseMutationOptions = ApolloCache> extends Omit, "mutation"> { +interface BaseMutationOptions = ApolloCache> extends MutationSharedOptions { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts - // - // (undocumented) client?: ApolloClient; - // (undocumented) ignoreResults?: boolean; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; - // (undocumented) onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; } +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } @@ -338,7 +330,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -378,7 +370,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -468,7 +460,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -495,44 +487,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -552,11 +533,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -566,21 +544,51 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) export type DataValue = QueryControls & Partial; +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -612,14 +620,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -629,13 +640,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -691,13 +701,11 @@ interface FetchMoreOptions { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -720,7 +728,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -729,8 +737,50 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; -// @public (undocumented) -export function graphql> & Partial>>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: React.ComponentType) => React.ComponentClass; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + +// @public @deprecated (undocumented) +export function graphql> & Partial>>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: ReactTypes.ComponentType) => ReactTypes.ComponentClass; // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -749,6 +799,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -817,15 +876,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -923,31 +980,20 @@ export interface MutateProps = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -965,21 +1011,11 @@ type MutationFunction = ApolloCache> extends BaseMutationOptions { - // (undocumented) mutation?: DocumentNode | TypedDocumentNode; } -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -999,20 +1035,23 @@ type MutationQueryReducersMap { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data?: TData | null; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) reset(): void; } +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1031,21 +1070,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1077,8 +1109,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1103,7 +1133,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1111,6 +1140,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1123,21 +1154,14 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } @@ -1155,7 +1179,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1192,6 +1219,9 @@ export interface OptionProps; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) export interface QueryControls { // (undocumented) @@ -1234,7 +1264,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1258,17 +1288,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1282,7 +1314,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 +1325,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1302,6 +1335,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) @@ -1331,7 +1366,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1345,7 +1382,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1388,7 +1425,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1400,27 +1436,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1525,6 +1553,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1550,7 +1599,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1566,15 +1615,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -1634,63 +1678,71 @@ interface UriFunction { (operation: Operation): string; } +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } -// @public (undocumented) -export function withApollo(WrappedComponent: React_2.ComponentType>>, operationOptions?: OperationOption): React_2.ComponentClass>; +// @public @deprecated (undocumented) +export function withApollo(WrappedComponent: ReactTypes.ComponentType>>, operationOptions?: OperationOption): ReactTypes.ComponentClass>; // @public (undocumented) export type WithApolloClient

= P & { client?: ApolloClient; }; -// @public (undocumented) -export function withMutation = {}, TGraphQLVariables extends OperationVariables = {}, TChildProps = MutateProps, TContext extends Record = DefaultContext, TCache extends ApolloCache = ApolloCache>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: React_2.ComponentType) => React_2.ComponentClass; +// @public @deprecated (undocumented) +export function withMutation = {}, TGraphQLVariables extends OperationVariables = {}, TChildProps = MutateProps, TContext extends Record = DefaultContext, TCache extends ApolloCache = ApolloCache>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: ReactTypes.ComponentType) => ReactTypes.ComponentClass; -// @public (undocumented) -export function withQuery = Record, TData extends object = {}, TGraphQLVariables extends object = {}, TChildProps extends object = DataProps>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: React_2.ComponentType) => React_2.ComponentClass; +// @public @deprecated (undocumented) +export function withQuery = Record, TData extends object = {}, TGraphQLVariables extends object = {}, TChildProps extends object = DataProps>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: ReactTypes.ComponentType) => ReactTypes.ComponentClass; -// @public (undocumented) -export function withSubscription>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: React_2.ComponentType) => React_2.ComponentClass; +// @public @deprecated (undocumented) +export function withSubscription>(document: DocumentNode, operationOptions?: OperationOption): (WrappedComponent: ReactTypes.ComponentType) => ReactTypes.ComponentClass; // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-react_hooks.md b/.api-reports/api-report-react_hooks.md index 110739c8df9..22d2d73624c 100644 --- a/.api-reports/api-report-react_hooks.md +++ b/.api-reports/api-report-react_hooks.md @@ -15,6 +15,7 @@ import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -35,10 +36,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -63,7 +67,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -75,6 +78,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -85,7 +92,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -95,30 +102,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -127,48 +129,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -176,38 +158,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -271,12 +257,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -288,16 +280,23 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { @@ -319,71 +318,55 @@ interface BackgroundQueryHookOptions = BackgroundQueryHookOptions, NoInfer>; +type BackgroundQueryHookOptionsNoInfer = BackgroundQueryHookOptions, NoInfer_2>; +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseMutationOptions = ApolloCache> extends Omit, "mutation"> { +interface BaseMutationOptions = ApolloCache> extends MutationSharedOptions { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts - // - // (undocumented) client?: ApolloClient; - // (undocumented) ignoreResults?: boolean; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; - // (undocumented) onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; } +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } // @public (undocumented) interface BaseSubscriptionOptions { - // (undocumented) client?: ApolloClient; - // (undocumented) context?: DefaultContext; // Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) onComplete?: () => void; // Warning: (ae-forgotten-export) The symbol "OnDataOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) onData?: (options: OnDataOptions) => any; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) + // @deprecated onSubscriptionComplete?: () => void; // Warning: (ae-forgotten-export) The symbol "OnSubscriptionDataOptions" needs to be exported by the entry point index.d.ts // - // (undocumented) + // @deprecated onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; - // (undocumented) shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean); - // (undocumented) skip?: boolean; - // (undocumented) variables?: TVariables; } @@ -392,7 +375,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -432,7 +415,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -484,13 +467,6 @@ namespace Cache_2 { import Fragment = DataProxy.Fragment; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -520,7 +496,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -541,44 +517,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -598,11 +563,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -612,15 +574,11 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } @@ -632,7 +590,7 @@ interface DataProxy { // Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends ReadonlyArray ? TItem[] extends T ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; // Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts // @@ -689,14 +647,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -706,13 +667,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -765,20 +725,15 @@ type FetchMoreFunction = (fetchMor }) => TData; }) => Promise>; -// @public (undocumented) -type FetchMoreOptions = Parameters["fetchMore"]>[0]; - // @public (undocumented) interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -801,7 +756,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -810,6 +765,48 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -827,6 +824,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -843,54 +849,6 @@ interface IncrementalPayload { path: Path; } -// @public (undocumented) -class InternalQueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReferenceOptions" needs to be exported by the entry point index.d.ts - constructor(observable: ObservableQuery, options: InternalQueryReferenceOptions); - // (undocumented) - applyOptions(watchQueryOptions: ObservedOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "ObservedOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) - didChangeOptions(watchQueryOptions: ObservedOptions): boolean; - // Warning: (ae-forgotten-export) The symbol "FetchMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchMore(options: FetchMoreOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts - // - // (undocumented) - readonly key: CacheKey; - // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts - // - // (undocumented) - listen(listener: Listener): () => void; - // (undocumented) - readonly observable: ObservableQuery; - // (undocumented) - promise: Promise>; - // (undocumented) - promiseCache?: Map>>; - // (undocumented) - refetch(variables: OperationVariables | undefined): Promise>; - // (undocumented) - result: ApolloQueryResult; - // (undocumented) - retain(): () => void; - // (undocumented) - get watchQueryOptions(): WatchQueryOptions; -} - -// @public (undocumented) -interface InternalQueryReferenceOptions { - // (undocumented) - autoDisposeTimeoutMs?: number; - // (undocumented) - key: CacheKey; - // (undocumented) - onDispose?: () => void; -} - // Warning: (ae-forgotten-export) The symbol "InternalRefetchQueryDescriptor" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesIncludeShorthand" needs to be exported by the entry point index.d.ts // @@ -952,32 +910,60 @@ interface LazyQueryHookExecOptions; } +// Warning: (ae-forgotten-export) The symbol "BaseQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface LazyQueryHookOptions extends Omit, "skip"> { +interface LazyQueryHookOptions extends BaseQueryOptions { + // @internal (undocumented) + defaultOptions?: Partial>; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; } // Warning: (ae-forgotten-export) The symbol "LazyQueryExecFunction" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type LazyQueryResultTuple = [LazyQueryExecFunction, QueryResult]; +type LazyQueryResultTuple = [ +execute: LazyQueryExecFunction, +result: QueryResult +]; + +// @public (undocumented) +type LoadableQueryHookFetchPolicy = Extract; + +// @public (undocumented) +interface LoadableQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: DefaultContext; + // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts + errorPolicy?: ErrorPolicy; + // Warning: (ae-forgotten-export) The symbol "LoadableQueryHookFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: LoadableQueryHookFetchPolicy; + queryKey?: string | number | any[]; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; +} +// Warning: (ae-forgotten-export) The symbol "OnlyRequiredProperties" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -type Listener = (promise: Promise>) => void; +export type LoadQueryFunction = (...args: [TVariables] extends [never] ? [] : {} extends OnlyRequiredProperties ? [variables?: TVariables] : [variables: TVariables]) => void; // @public (undocumented) class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -1063,31 +1049,19 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; - // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -1098,7 +1072,6 @@ type MutationFetchPolicy = Extract; // // @public (undocumented) interface MutationFunctionOptions = ApolloCache> extends BaseMutationOptions { - // (undocumented) mutation?: DocumentNode | TypedDocumentNode; } @@ -1106,17 +1079,8 @@ interface MutationFunctionOptions = ApolloCache> extends BaseMutationOptions { } -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1136,20 +1100,23 @@ type MutationQueryReducersMap { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data?: TData | null; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) reset(): void; } +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1167,8 +1134,8 @@ interface MutationStoreValue { // // @public (undocumented) type MutationTuple = ApolloCache> = [ -(options?: MutationFunctionOptions) => Promise>, -MutationResult +mutate: (options?: MutationFunctionOptions) => Promise>, +result: MutationResult ]; // @public (undocumented) @@ -1177,21 +1144,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1213,8 +1173,8 @@ type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) -type NoInfer = [T][T extends any ? 0 : never]; +// @public +type NoInfer_2 = [T][T extends any ? 0 : never]; // @public (undocumented) class ObservableQuery extends Observable> { @@ -1223,7 +1183,6 @@ class ObservableQuery; }); - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1248,7 +1207,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1256,6 +1214,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1268,34 +1228,34 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; - -// @public (undocumented) -const OBSERVED_CHANGED_OPTIONS: readonly ["canonizeResults", "context", "errorPolicy", "fetchPolicy", "refetchWritePolicy", "returnPartialData"]; - -// Warning: (ae-forgotten-export) The symbol "OBSERVED_CHANGED_OPTIONS" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -type ObservedOptions = Pick; +interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} // @public (undocumented) interface OnDataOptions { @@ -1307,6 +1267,11 @@ interface OnDataOptions { data: SubscriptionResult; } +// @public +type OnlyRequiredProperties = { + [K in keyof T as {} extends Pick ? never : K]: T[K]; +}; + // @public (undocumented) type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1329,7 +1294,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1344,19 +1312,14 @@ type Path = ReadonlyArray; type Primitive = null | undefined | string | number | boolean | symbol | bigint; // @public (undocumented) -const QUERY_REFERENCE_SYMBOL: unique symbol; +const QUERY_REF_BRAND: unique symbol; -// Warning: (ae-forgotten-export) The symbol "BaseQueryOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -interface QueryFunctionOptions extends BaseQueryOptions { - // (undocumented) +interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -1380,7 +1343,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1404,17 +1367,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1428,7 +1393,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 +1404,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1448,6 +1414,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) @@ -1477,7 +1445,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1491,7 +1461,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1534,7 +1504,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1546,57 +1515,39 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } -// @public (undocumented) -interface QueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; +// @public +interface QueryRef { + // @internal (undocumented) + [QUERY_REF_BRAND]?(variables: TVariables): TData; } // Warning: (ae-forgotten-export) The symbol "ObservableQueryFields" needs to be exported by the entry point index.d.ts // // @public (undocumented) interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } @@ -1690,6 +1641,9 @@ type RefetchWritePolicy = "merge" | "overwrite"; // @public (undocumented) type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; +// @public (undocumented) +type ResetFunction = () => void; + // @public (undocumented) type Resolver = (rootValue?: any, args?: any, context?: any, info?: { field: FieldNode; @@ -1721,6 +1675,26 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1752,7 +1726,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1777,27 +1751,19 @@ interface SubscriptionHookOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // @public (undocumented) interface SubscriptionResult { - // (undocumented) data?: TData; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) + // @internal (undocumented) variables?: TVariables; } @@ -1805,15 +1771,20 @@ interface SubscriptionResult { type SuspenseQueryHookFetchPolicy = Extract; // @public (undocumented) -interface SuspenseQueryHookOptions extends Pick, "client" | "variables" | "errorPolicy" | "context" | "canonizeResults" | "returnPartialData" | "refetchWritePolicy"> { +interface SuspenseQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "SuspenseQueryHookFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchPolicy?: SuspenseQueryHookFetchPolicy; - // (undocumented) queryKey?: string | number | any[]; - // (undocumented) + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + // @deprecated skip?: boolean; + variables?: TVariables; } // @public (undocumented) @@ -1870,11 +1841,11 @@ interface UriFunction { export function useApolloClient(override?: ApolloClient): ApolloClient; // Warning: (ae-forgotten-export) The symbol "BackgroundQueryHookOptionsNoInfer" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "QueryReference" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "QueryRef" needs to be exported by the entry point index.d.ts // // @public (undocumented) export function useBackgroundQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer & TOptions): [ -(QueryReference | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData> | (TOptions["skip"] extends boolean ? undefined : never)), +(QueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables> | (TOptions["skip"] extends boolean ? undefined : never)), UseBackgroundQueryResult ]; @@ -1883,7 +1854,7 @@ export function useBackgroundQuery | undefined>, +QueryRef | undefined, TVariables>, UseBackgroundQueryResult ]; @@ -1891,7 +1862,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { errorPolicy: "ignore" | "all"; }): [ -QueryReference, +QueryRef, UseBackgroundQueryResult ]; @@ -1900,7 +1871,7 @@ export function useBackgroundQuery> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; @@ -1908,7 +1879,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; }): [ -QueryReference>, +QueryRef, TVariables>, UseBackgroundQueryResult ]; @@ -1916,12 +1887,12 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { skip: boolean; }): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; // @public (undocumented) -export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryReference, UseBackgroundQueryResult]; +export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryRef, UseBackgroundQueryResult]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken): [undefined, UseBackgroundQueryResult]; @@ -1930,13 +1901,13 @@ export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; })): [ -QueryReference> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | BackgroundQueryHookOptionsNoInfer): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; @@ -1950,7 +1921,8 @@ export type UseBackgroundQueryResult(options: UseFragmentOptions): UseFragmentResult; // @public (undocumented) -export interface UseFragmentOptions extends Omit, NoInfer>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { +export interface UseFragmentOptions extends Omit, NoInfer_2>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { + client?: ApolloClient; // (undocumented) from: StoreObject | Reference | string; // (undocumented) @@ -1970,83 +1942,124 @@ export type UseFragmentResult = { // Warning: (ae-forgotten-export) The symbol "LazyQueryResultTuple" needs to be exported by the entry point index.d.ts // +// @public +export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer_2>): LazyQueryResultTuple; + +// Warning: (ae-forgotten-export) The symbol "LoadableQueryHookOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions & TOptions): UseLoadableQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables>; + +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult | undefined, TVariables>; + // @public (undocumented) -export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer>): LazyQueryResultTuple; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult; + +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; +}): UseLoadableQueryResult, TVariables>; + +// @public +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions): UseLoadableQueryResult; + +// @public (undocumented) +export type UseLoadableQueryResult = [ +loadQuery: LoadQueryFunction, +queryRef: QueryRef | null, +handlers: { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; + reset: ResetFunction; +} +]; // Warning: (ae-forgotten-export) The symbol "MutationHookOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationTuple" needs to be exported by the entry point index.d.ts // -// @public (undocumented) -export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer, TContext, TCache>): MutationTuple; +// @public +export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer_2, TContext, TCache>): MutationTuple; + +// @public +export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer_2>): QueryResult; + +// @public +export function useQueryRefHandlers(queryRef: QueryRef): UseQueryRefHandlersResult; // @public (undocumented) -export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer>): QueryResult; +export interface UseQueryRefHandlersResult { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; +} // Warning: (ae-forgotten-export) The symbol "ReactiveVar" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export function useReactiveVar(rv: ReactiveVar): T; // @public (undocumented) -export function useReadQuery(queryRef: QueryReference): UseReadQueryResult; +export function useReadQuery(queryRef: QueryRef): UseReadQueryResult; // @public (undocumented) export interface UseReadQueryResult { - // (undocumented) data: TData; - // (undocumented) error: ApolloError | undefined; - // (undocumented) networkStatus: NetworkStatus; } // Warning: (ae-forgotten-export) The symbol "SubscriptionHookOptions" needs to be exported by the entry point index.d.ts // -// @public (undocumented) -export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer>): SubscriptionResult; +// @public +export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): SubscriptionResult; // Warning: (ae-forgotten-export) The symbol "SuspenseQueryHookOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; +export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; returnPartialData: true; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; }): UseSuspenseQueryResult, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; })): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) export interface UseSuspenseQueryResult { @@ -2068,48 +2081,57 @@ export interface UseSuspenseQueryResult; } +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/react/hooks/useBackgroundQuery.ts:24:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts -// src/react/hooks/useBackgroundQuery.ts:25:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts -// src/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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/react/hooks/useBackgroundQuery.ts:29:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useBackgroundQuery.ts:30:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useLoadableQuery.ts:107:1 - (ae-forgotten-export) The symbol "ResetFunction" 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_internal.md b/.api-reports/api-report-react_internal.md new file mode 100644 index 00000000000..8f490b544de --- /dev/null +++ b/.api-reports/api-report-react_internal.md @@ -0,0 +1,2142 @@ +## API Report File for "@apollo/client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ASTNode } from 'graphql'; +import type { DocumentNode } from 'graphql'; +import type { ExecutionResult } from 'graphql'; +import type { FieldNode } from 'graphql'; +import type { FragmentDefinitionNode } from 'graphql'; +import type { GraphQLError } from 'graphql'; +import type { GraphQLErrorExtensions } from 'graphql'; +import { Observable } from 'zen-observable-ts'; +import type { Observer } from 'zen-observable-ts'; +import type { Subscriber } from 'zen-observable-ts'; +import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; +import { TypedDocumentNode } from '@graphql-typed-document-node/core'; + +// Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "StoreObjectValueMaybeReference" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type AllFieldsModifier> = Modifier> : never>; + +// Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +abstract class ApolloCache implements DataProxy { + // (undocumented) + readonly assumeImmutableResults: boolean; + // (undocumented) + batch(options: Cache_2.BatchOptions): U; + // (undocumented) + abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; + // (undocumented) + abstract evict(options: Cache_2.EvictOptions): boolean; + abstract extract(optimistic?: boolean): TSerialized; + // (undocumented) + gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; + // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts + // + // (undocumented) + identify(object: StoreObject | Reference): string | undefined; + // (undocumented) + modify = Record>(options: Cache_2.ModifyOptions): boolean; + // Warning: (ae-forgotten-export) The symbol "Transaction" needs to be exported by the entry point index.d.ts + // + // (undocumented) + abstract performTransaction(transaction: Transaction, optimisticId?: string | null): void; + // Warning: (ae-forgotten-export) The symbol "Cache_2" needs to be exported by the entry point index.d.ts + // + // (undocumented) + abstract read(query: Cache_2.ReadOptions): TData | null; + // (undocumented) + readFragment(options: Cache_2.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; + // (undocumented) + readQuery(options: Cache_2.ReadQueryOptions, optimistic?: boolean): QueryType | null; + // (undocumented) + recordOptimisticTransaction(transaction: Transaction, optimisticId: string): void; + // (undocumented) + abstract removeOptimistic(id: string): void; + // (undocumented) + abstract reset(options?: Cache_2.ResetOptions): Promise; + abstract restore(serializedState: TSerialized): ApolloCache; + // (undocumented) + transformDocument(document: DocumentNode): DocumentNode; + // (undocumented) + transformForLink(document: DocumentNode): DocumentNode; + // (undocumented) + updateFragment(options: Cache_2.UpdateFragmentOptions, update: (data: TData | null) => TData | null | void): TData | null; + // (undocumented) + updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; + // (undocumented) + abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; + // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + abstract write(write: Cache_2.WriteOptions): Reference | undefined; + // (undocumented) + writeFragment({ id, data, fragment, fragmentName, ...options }: Cache_2.WriteFragmentOptions): Reference | undefined; + // (undocumented) + writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; +} + +// @public +class ApolloClient implements DataProxy { + // (undocumented) + __actionHookForDevTools(cb: () => any): void; + constructor(options: ApolloClientOptions); + // Warning: (ae-forgotten-export) The symbol "GraphQLRequest" needs to be exported by the entry point index.d.ts + // + // (undocumented) + __requestRaw(payload: GraphQLRequest): Observable; + // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts + addResolvers(resolvers: Resolvers | Resolvers[]): void; + // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts + // + // (undocumented) + cache: ApolloCache; + clearStore(): Promise; + // (undocumented) + get defaultContext(): Partial; + // (undocumented) + defaultOptions: DefaultOptions; + // (undocumented) + disableNetworkFetches: boolean; + // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts + get documentTransform(): DocumentTransform; + extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; + // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts + getObservableQueries(include?: RefetchQueriesInclude): Map>; + getResolvers(): Resolvers; + // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts + // + // (undocumented) + link: ApolloLink; + // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts + mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; + onClearStore(cb: () => Promise): () => void; + onResetStore(cb: () => Promise): () => void; + // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts + query(options: QueryOptions): Promise>; + // (undocumented) + queryDeduplication: boolean; + readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; + readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; + reFetchObservableQueries(includeStandby?: boolean): Promise[]>; + // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts + refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; + resetStore(): Promise[] | null>; + restore(serializedState: TCacheShape): ApolloCache; + setLink(newLink: ApolloLink): void; + // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts + setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; + setResolvers(resolvers: Resolvers | Resolvers[]): void; + stop(): void; + // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts + subscribe(options: SubscriptionOptions): Observable>; + // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly typeDefs: ApolloClientOptions["typeDefs"]; + // (undocumented) + version: string; + watchFragment(options: WatchFragmentOptions): Observable>; + // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts + watchQuery(options: WatchQueryOptions): ObservableQuery; + writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; + writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; +} + +// @public (undocumented) +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) + credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; + headers?: Record; + link?: ApolloLink; + name?: string; + queryDeduplication?: boolean; + // (undocumented) + resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) + typeDefs?: string | string[] | DocumentNode | DocumentNode[]; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; + version?: string; +} + +// @public (undocumented) +class ApolloError extends Error { + // Warning: (ae-forgotten-export) The symbol "ApolloErrorOptions" needs to be exported by the entry point index.d.ts + constructor({ graphQLErrors, protocolErrors, clientErrors, networkError, errorMessage, extraInfo, }: ApolloErrorOptions); + // (undocumented) + clientErrors: ReadonlyArray; + // (undocumented) + extraInfo: any; + // Warning: (ae-forgotten-export) The symbol "GraphQLErrors" needs to be exported by the entry point index.d.ts + // + // (undocumented) + graphQLErrors: GraphQLErrors; + // (undocumented) + message: string; + // (undocumented) + name: string; + // Warning: (ae-forgotten-export) The symbol "ServerParseError" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ServerError" needs to be exported by the entry point index.d.ts + // + // (undocumented) + networkError: Error | ServerParseError | ServerError | null; + // (undocumented) + protocolErrors: ReadonlyArray<{ + message: string; + extensions?: GraphQLErrorExtensions[]; + }>; +} + +// @public (undocumented) +interface ApolloErrorOptions { + // (undocumented) + clientErrors?: ReadonlyArray; + // (undocumented) + errorMessage?: string; + // (undocumented) + extraInfo?: any; + // (undocumented) + graphQLErrors?: ReadonlyArray; + // (undocumented) + networkError?: Error | ServerParseError | ServerError | null; + // (undocumented) + protocolErrors?: ReadonlyArray<{ + message: string; + extensions?: GraphQLErrorExtensions[]; + }>; +} + +// @public (undocumented) +class ApolloLink { + constructor(request?: RequestHandler); + // (undocumented) + static concat(first: ApolloLink | RequestHandler, second: ApolloLink | RequestHandler): ApolloLink; + // (undocumented) + concat(next: ApolloLink | RequestHandler): ApolloLink; + // (undocumented) + static empty(): ApolloLink; + // (undocumented) + static execute(link: ApolloLink, operation: GraphQLRequest): Observable; + // Warning: (ae-forgotten-export) The symbol "RequestHandler" needs to be exported by the entry point index.d.ts + // + // (undocumented) + static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; + // (undocumented) + protected onError(error: any, observer?: Observer): false | void; + // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts + // + // (undocumented) + request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; + // (undocumented) + setOnError(fn: ApolloLink["onError"]): this; + // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts + // + // (undocumented) + static split(test: (op: Operation) => boolean, left: ApolloLink | RequestHandler, right?: ApolloLink | RequestHandler): ApolloLink; + // (undocumented) + split(test: (op: Operation) => boolean, left: ApolloLink | RequestHandler, right?: ApolloLink | RequestHandler): ApolloLink; +} + +// @public (undocumented) +interface ApolloQueryResult { + // (undocumented) + data: T; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts + error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) + loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) + networkStatus: NetworkStatus; + // (undocumented) + partial?: boolean; +} + +// Warning: (ae-forgotten-export) The symbol "WrappedQueryRef" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function assertWrappedQueryRef(queryRef: QueryRef): asserts queryRef is WrappedQueryRef; + +// @public (undocumented) +export function assertWrappedQueryRef(queryRef: QueryRef | undefined | null): asserts queryRef is WrappedQueryRef | undefined | null; + +// @public +type AsStoreObject = { + [K in keyof T]: T[K]; +}; + +// Warning: (ae-forgotten-export) The symbol "WatchQueryFetchPolicy" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type BackgroundQueryHookFetchPolicy = Extract; + +// Warning: (ae-forgotten-export) The symbol "QueryHookOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface BackgroundQueryHookOptions extends Pick, "client" | "variables" | "errorPolicy" | "context" | "canonizeResults" | "returnPartialData" | "refetchWritePolicy"> { + // Warning: (ae-forgotten-export) The symbol "BackgroundQueryHookFetchPolicy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + fetchPolicy?: BackgroundQueryHookFetchPolicy; + // (undocumented) + queryKey?: string | number | any[]; + // @deprecated + skip?: boolean; +} + +// Warning: (ae-forgotten-export) The symbol "BackgroundQueryHookOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "NoInfer_2" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type BackgroundQueryHookOptionsNoInfer = BackgroundQueryHookOptions, NoInfer_2>; + +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface BaseQueryOptions extends SharedWatchQueryOptions { + // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts + client?: ApolloClient; + context?: DefaultContext; + ssr?: boolean; +} + +// @public (undocumented) +namespace Cache_2 { + // (undocumented) + interface BatchOptions, TUpdateResult = void> { + // (undocumented) + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; + // (undocumented) + optimistic?: string | boolean; + // (undocumented) + removeOptimistic?: string; + // (undocumented) + update(cache: TCache): TUpdateResult; + } + // Warning: (ae-forgotten-export) The symbol "Cache_2" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface DiffOptions extends Omit, "rootId"> { + } + // (undocumented) + interface EvictOptions { + // (undocumented) + args?: Record; + // (undocumented) + broadcast?: boolean; + // (undocumented) + fieldName?: string; + // (undocumented) + id?: string; + } + // (undocumented) + interface ModifyOptions = Record> { + // (undocumented) + broadcast?: boolean; + // Warning: (ae-forgotten-export) The symbol "Modifiers" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "AllFieldsModifier" needs to be exported by the entry point index.d.ts + // + // (undocumented) + fields: Modifiers | AllFieldsModifier; + // (undocumented) + id?: string; + // (undocumented) + optimistic?: boolean; + } + // (undocumented) + interface ReadOptions extends DataProxy.Query { + // @deprecated (undocumented) + canonizeResults?: boolean; + // (undocumented) + optimistic: boolean; + // (undocumented) + previousResult?: any; + // (undocumented) + returnPartialData?: boolean; + // (undocumented) + rootId?: string; + } + // (undocumented) + interface ResetOptions { + // (undocumented) + discardWatches?: boolean; + } + // (undocumented) + type WatchCallback = (diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult) => void; + // Warning: (ae-forgotten-export) The symbol "Cache_2" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface WatchOptions extends DiffOptions { + // Warning: (ae-forgotten-export) The symbol "Cache_2" needs to be exported by the entry point index.d.ts + // + // (undocumented) + callback: WatchCallback; + // (undocumented) + immediate?: boolean; + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + lastDiff?: DiffResult; + // (undocumented) + watcher?: object; + } + // (undocumented) + interface WriteOptions extends Omit, "id">, Omit, "data"> { + // (undocumented) + dataId?: string; + // (undocumented) + result: TResult; + } + import DiffResult = DataProxy.DiffResult; + import ReadQueryOptions = DataProxy.ReadQueryOptions; + import ReadFragmentOptions = DataProxy.ReadFragmentOptions; + import WriteQueryOptions = DataProxy.WriteQueryOptions; + import WriteFragmentOptions = DataProxy.WriteFragmentOptions; + import UpdateQueryOptions = DataProxy.UpdateQueryOptions; + import UpdateFragmentOptions = DataProxy.UpdateFragmentOptions; + import Fragment = DataProxy.Fragment; +} + +// @public (undocumented) +export type CacheKey = [ +query: DocumentNode, +stringifiedVariables: string, +...queryKey: any[] +]; + +// @public (undocumented) +const enum CacheWriteBehavior { + // (undocumented) + FORBID = 0, + // (undocumented) + MERGE = 2, + // (undocumented) + OVERWRITE = 1 +} + +// Warning: (ae-forgotten-export) The symbol "StoreValue" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type CanReadFunction = (value: StoreValue) => boolean; + +// @public (undocumented) +class Concast extends Observable { + // Warning: (ae-forgotten-export) The symbol "MaybeAsync" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ConcastSourcesIterable" needs to be exported by the entry point index.d.ts + constructor(sources: MaybeAsync> | Subscriber); + // (undocumented) + addObserver(observer: Observer): void; + // Warning: (ae-forgotten-export) The symbol "NextResultListener" needs to be exported by the entry point index.d.ts + // + // (undocumented) + beforeNext(callback: NextResultListener): void; + // (undocumented) + cancel: (reason: any) => void; + // (undocumented) + readonly promise: Promise; + // (undocumented) + removeObserver(observer: Observer): void; +} + +// Warning: (ae-forgotten-export) The symbol "Source" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type ConcastSourcesIterable = Iterable>; + +// @public (undocumented) +namespace DataProxy { + // (undocumented) + type DiffResult = { + result?: T; + complete?: boolean; + missing?: MissingFieldError[]; + fromOptimisticTransaction?: boolean; + }; + // (undocumented) + interface Fragment { + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + id?: string; + variables?: TVariables; + } + // (undocumented) + interface Query { + id?: string; + query: DocumentNode | TypedDocumentNode; + variables?: TVariables; + } + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface ReadFragmentOptions extends Fragment { + // @deprecated + canonizeResults?: boolean; + optimistic?: boolean; + returnPartialData?: boolean; + } + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface ReadQueryOptions extends Query { + // @deprecated + canonizeResults?: boolean; + optimistic?: boolean; + returnPartialData?: boolean; + } + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface UpdateFragmentOptions extends Omit & WriteFragmentOptions, "data"> { + } + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface UpdateQueryOptions extends Omit & WriteQueryOptions, "data"> { + } + // (undocumented) + interface WriteFragmentOptions extends Fragment, WriteOptions { + } + // (undocumented) + interface WriteOptions { + broadcast?: boolean; + data: TData; + overwrite?: boolean; + } + // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interface WriteQueryOptions extends Query, WriteOptions { + } +} + +// @public +interface DataProxy { + readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; + readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; + writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; + writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; +} + +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + +// @public (undocumented) +interface DefaultContext extends Record { +} + +// @public (undocumented) +interface DefaultOptions { + // (undocumented) + mutate?: Partial>; + // (undocumented) + query?: Partial>; + // (undocumented) + watchQuery?: Partial>; +} + +// @public (undocumented) +interface DeleteModifier { + // (undocumented) + [_deleteModifier]: true; +} + +// @public (undocumented) +const _deleteModifier: unique symbol; + +// @public (undocumented) +class DocumentTransform { + // Warning: (ae-forgotten-export) The symbol "TransformFn" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "DocumentTransformOptions" needs to be exported by the entry point index.d.ts + constructor(transform: TransformFn, options?: DocumentTransformOptions); + // (undocumented) + concat(otherTransform: DocumentTransform): DocumentTransform; + // (undocumented) + static identity(): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; + // (undocumented) + transformDocument(document: DocumentNode): DocumentNode; +} + +// @public (undocumented) +type DocumentTransformCacheKey = ReadonlyArray; + +// @public (undocumented) +interface DocumentTransformOptions { + cache?: boolean; + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts + getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; +} + +// @public +type ErrorPolicy = "none" | "ignore" | "all"; + +// Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface ExecutionPatchIncrementalResult, TExtensions = Record> extends ExecutionPatchResultBase { + // (undocumented) + data?: never; + // (undocumented) + errors?: never; + // (undocumented) + extensions?: never; + // Warning: (ae-forgotten-export) The symbol "IncrementalPayload" needs to be exported by the entry point index.d.ts + // + // (undocumented) + incremental?: IncrementalPayload[]; +} + +// @public (undocumented) +interface ExecutionPatchInitialResult, TExtensions = Record> extends ExecutionPatchResultBase { + // (undocumented) + data: TData | null | undefined; + // (undocumented) + errors?: ReadonlyArray; + // (undocumented) + extensions?: TExtensions; + // (undocumented) + incremental?: never; +} + +// Warning: (ae-forgotten-export) The symbol "ExecutionPatchInitialResult" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutionPatchIncrementalResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type ExecutionPatchResult, TExtensions = Record> = ExecutionPatchInitialResult | ExecutionPatchIncrementalResult; + +// @public (undocumented) +interface ExecutionPatchResultBase { + // (undocumented) + hasNext?: boolean; +} + +// Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type FetchMoreFunction = (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TData; + variables: TVariables; + }) => TData; +}) => Promise>; + +// @public (undocumented) +type FetchMoreOptions = Parameters["fetchMore"]>[0]; + +// @public (undocumented) +interface FetchMoreQueryOptions { + // (undocumented) + context?: DefaultContext; + query?: DocumentNode | TypedDocumentNode; + variables?: Partial; +} + +// @public +type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; + +// Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutionPatchResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type FetchResult, TContext = Record, TExtensions = Record> = SingleExecutionResult | ExecutionPatchResult; + +// @public (undocumented) +interface FieldSpecifier { + // (undocumented) + args?: Record; + // (undocumented) + field?: FieldNode; + // (undocumented) + fieldName: string; + // (undocumented) + typename?: string; + // (undocumented) + variables?: Record; +} + +// @public +interface FragmentMap { + // (undocumented) + [fragmentName: string]: FragmentDefinitionNode; +} + +// @public (undocumented) +type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; + +// @public (undocumented) +interface FulfilledPromise extends Promise { + // (undocumented) + status: "fulfilled"; + // (undocumented) + value: TValue; +} + +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + +// Warning: (ae-forgotten-export) The symbol "SuspenseCache" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function getSuspenseCache(client: ApolloClient & { + [suspenseCacheSymbol]?: SuspenseCache; +}): SuspenseCache; + +// Warning: (ae-forgotten-export) The symbol "QueryRefPromise" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function getWrappedPromise(queryRef: WrappedQueryRef): QueryRefPromise; + +// @public (undocumented) +type GraphQLErrors = ReadonlyArray; + +// @public (undocumented) +interface GraphQLRequest> { + // (undocumented) + context?: DefaultContext; + // (undocumented) + extensions?: Record; + // (undocumented) + operationName?: string; + // (undocumented) + query: DocumentNode; + // (undocumented) + variables?: TVariables; +} + +// Warning: (ae-forgotten-export) The symbol "WrappableHooks" needs to be exported by the entry point index.d.ts +// +// @internal +export type HookWrappers = { + [K in keyof WrappableHooks]?: (originalHook: WrappableHooks[K]) => WrappableHooks[K]; +}; + +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + +// @public (undocumented) +interface IncrementalPayload { + // (undocumented) + data: TData | null; + // (undocumented) + errors?: ReadonlyArray; + // (undocumented) + extensions?: TExtensions; + // (undocumented) + label?: string; + // Warning: (ae-forgotten-export) The symbol "Path" needs to be exported by the entry point index.d.ts + // + // (undocumented) + path: Path; +} + +// @public (undocumented) +export class InternalQueryReference { + // Warning: (ae-forgotten-export) The symbol "InternalQueryReferenceOptions" needs to be exported by the entry point index.d.ts + constructor(observable: ObservableQuery, options: InternalQueryReferenceOptions); + // (undocumented) + applyOptions(watchQueryOptions: ObservedOptions): QueryRefPromise; + // Warning: (ae-forgotten-export) The symbol "ObservedOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + didChangeOptions(watchQueryOptions: ObservedOptions): boolean; + // (undocumented) + get disposed(): boolean; + // Warning: (ae-forgotten-export) The symbol "FetchMoreOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + fetchMore(options: FetchMoreOptions): Promise>; + // (undocumented) + readonly key: QueryKey; + // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts + // + // (undocumented) + listen(listener: Listener): () => void; + // (undocumented) + readonly observable: ObservableQuery; + // (undocumented) + promise: QueryRefPromise; + // (undocumented) + refetch(variables: OperationVariables | undefined): Promise>; + // (undocumented) + reinitialize(): void; + // (undocumented) + result: ApolloQueryResult; + // (undocumented) + retain(): () => void; + // (undocumented) + softRetain(): () => void; + // (undocumented) + get watchQueryOptions(): WatchQueryOptions; +} + +// @public (undocumented) +interface InternalQueryReferenceOptions { + // (undocumented) + autoDisposeTimeoutMs?: number; + // (undocumented) + onDispose?: () => void; +} + +// Warning: (ae-forgotten-export) The symbol "InternalRefetchQueryDescriptor" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RefetchQueriesIncludeShorthand" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type InternalRefetchQueriesInclude = InternalRefetchQueryDescriptor[] | RefetchQueriesIncludeShorthand; + +// Warning: (ae-forgotten-export) The symbol "InternalRefetchQueriesResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type InternalRefetchQueriesMap = Map, InternalRefetchQueriesResult>; + +// @public (undocumented) +interface InternalRefetchQueriesOptions, TResult> extends Omit, "include"> { + // Warning: (ae-forgotten-export) The symbol "InternalRefetchQueriesInclude" needs to be exported by the entry point index.d.ts + // + // (undocumented) + include?: InternalRefetchQueriesInclude; + // (undocumented) + removeOptimistic?: string; +} + +// @public (undocumented) +type InternalRefetchQueriesResult = TResult extends boolean ? Promise> : TResult; + +// Warning: (ae-forgotten-export) The symbol "RefetchQueryDescriptor" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type InternalRefetchQueryDescriptor = RefetchQueryDescriptor | QueryOptions; + +// @public (undocumented) +interface InvalidateModifier { + // (undocumented) + [_invalidateModifier]: true; +} + +// @public (undocumented) +const _invalidateModifier: unique symbol; + +// @public (undocumented) +function isReference(obj: any): obj is Reference; + +// Warning: (ae-forgotten-export) The symbol "UnionToIntersection" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UnionForAny" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type IsStrictlyAny = UnionToIntersection> extends never ? true : false; + +// @public (undocumented) +type Listener = (promise: QueryRefPromise) => void; + +// @public (undocumented) +class LocalState { + // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts + constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); + // (undocumented) + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; + // (undocumented) + addResolvers(resolvers: Resolvers | Resolvers[]): void; + // (undocumented) + clientQuery(document: DocumentNode): DocumentNode | null; + // (undocumented) + getFragmentMatcher(): FragmentMatcher | undefined; + // (undocumented) + getResolvers(): Resolvers; + // (undocumented) + prepareContext(context?: Record): { + cache: ApolloCache; + getCacheKey(obj: StoreObject): string | undefined; + }; + // (undocumented) + runResolvers({ document, remoteResult, context, variables, onlyRunForcedResolvers, }: { + document: DocumentNode | null; + remoteResult: FetchResult; + context?: Record; + variables?: Record; + onlyRunForcedResolvers?: boolean; + }): Promise>; + // (undocumented) + serverQuery(document: DocumentNode): DocumentNode | null; + // (undocumented) + setFragmentMatcher(fragmentMatcher: FragmentMatcher): void; + // (undocumented) + setResolvers(resolvers: Resolvers | Resolvers[]): void; + // (undocumented) + shouldForceResolvers(document: ASTNode): boolean; +} + +// @public (undocumented) +type LocalStateOptions = { + cache: ApolloCache; + client?: ApolloClient; + resolvers?: Resolvers | Resolvers[]; + fragmentMatcher?: FragmentMatcher; +}; + +// @public (undocumented) +type MaybeAsync = T | PromiseLike; + +// @public (undocumented) +class MissingFieldError extends Error { + constructor(message: string, path: MissingTree | Array, query: DocumentNode, variables?: Record | undefined); + // (undocumented) + readonly message: string; + // (undocumented) + readonly missing: MissingTree; + // Warning: (ae-forgotten-export) The symbol "MissingTree" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly path: MissingTree | Array; + // (undocumented) + readonly query: DocumentNode; + // (undocumented) + readonly variables?: Record | undefined; +} + +// @public (undocumented) +type MissingTree = string | { + readonly [key: string]: MissingTree; +}; + +// Warning: (ae-forgotten-export) The symbol "ModifierDetails" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeleteModifier" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "InvalidateModifier" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type Modifier = (value: T, details: ModifierDetails) => T | DeleteModifier | InvalidateModifier; + +// @public (undocumented) +type ModifierDetails = { + DELETE: DeleteModifier; + INVALIDATE: InvalidateModifier; + fieldName: string; + storeFieldName: string; + readField: ReadFieldFunction; + canRead: CanReadFunction; + isReference: typeof isReference; + toReference: ToReferenceFunction; + storage: StorageType; +}; + +// @public (undocumented) +type Modifiers = Record> = Partial<{ + [FieldName in keyof T]: Modifier>>; +}>; + +// @public (undocumented) +interface MutationBaseOptions = ApolloCache> { + awaitRefetchQueries?: boolean; + context?: TContext; + // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts + errorPolicy?: ErrorPolicy; + // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts + onQueryUpdated?: OnQueryUpdated; + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); + refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; + // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts + update?: MutationUpdaterFunction; + // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts + updateQueries?: MutationQueryReducersMap; + variables?: TVariables; +} + +// Warning: (ae-forgotten-export) The symbol "FetchPolicy" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type MutationFetchPolicy = Extract; + +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { + mutation: DocumentNode | TypedDocumentNode; +} + +// @public (undocumented) +type MutationQueryReducer = (previousResult: Record, options: { + mutationResult: FetchResult; + queryName: string | undefined; + queryVariables: Record; +}) => Record; + +// @public (undocumented) +type MutationQueryReducersMap = { + [queryName: string]: MutationQueryReducer; +}; + +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + +// @public (undocumented) +interface MutationStoreValue { + // (undocumented) + error: Error | null; + // (undocumented) + loading: boolean; + // (undocumented) + mutation: DocumentNode; + // (undocumented) + variables: Record; +} + +// @public (undocumented) +type MutationUpdaterFunction> = (cache: TCache, result: Omit, "context">, options: { + context?: TContext; + variables?: TVariables; +}) => void; + +// @public +enum NetworkStatus { + error = 8, + fetchMore = 3, + loading = 1, + poll = 6, + ready = 7, + refetch = 4, + setVariables = 2 +} + +// @public (undocumented) +interface NextFetchPolicyContext { + // (undocumented) + initialFetchPolicy: WatchQueryFetchPolicy; + // (undocumented) + observable: ObservableQuery; + // (undocumented) + options: WatchQueryOptions; + // (undocumented) + reason: "after-fetch" | "variables-changed"; +} + +// @public (undocumented) +type NextLink = (operation: Operation) => Observable; + +// @public (undocumented) +type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; + +// @public +type NoInfer_2 = [T][T extends any ? 0 : never]; + +// @public (undocumented) +class ObservableQuery extends Observable> { + constructor({ queryManager, queryInfo, options, }: { + queryManager: QueryManager; + queryInfo: QueryInfo; + options: WatchQueryOptions; + }); + fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }): Promise>; + // (undocumented) + getCurrentResult(saveAsLastResult?: boolean): ApolloQueryResult; + // (undocumented) + getLastError(variablesMustMatch?: boolean): ApolloError | undefined; + // (undocumented) + getLastResult(variablesMustMatch?: boolean): ApolloQueryResult | undefined; + // (undocumented) + hasObservers(): boolean; + // (undocumented) + isDifferentFromLastResult(newResult: ApolloQueryResult, variables?: TVariables): boolean | undefined; + // (undocumented) + readonly options: WatchQueryOptions; + // (undocumented) + get query(): TypedDocumentNode; + // (undocumented) + readonly queryId: string; + // (undocumented) + readonly queryName?: string; + refetch(variables?: Partial): Promise>; + // (undocumented) + reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; + // Warning: (ae-forgotten-export) The symbol "Concast" needs to be exported by the entry point index.d.ts + // + // (undocumented) + reobserveAsConcast(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; + // (undocumented) + resetLastResults(): void; + // (undocumented) + resetQueryStoreErrors(): void; + // (undocumented) + resubscribeAfterError(onNext: (value: ApolloQueryResult) => void, onError?: (error: any) => void, onComplete?: () => void): Subscription; + // (undocumented) + resubscribeAfterError(observer: Observer>): Subscription; + // (undocumented) + result(): Promise>; + // (undocumented) + setOptions(newOptions: Partial>): Promise>; + setVariables(variables: TVariables): Promise | void>; + // (undocumented) + silentSetOptions(newOptions: Partial>): void; + startPolling(pollInterval: number): void; + stopPolling(): void; + // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts + subscribeToMore(options: SubscribeToMoreOptions): () => void; + updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; + get variables(): TVariables | undefined; +} + +// @public (undocumented) +interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} + +// @public (undocumented) +const OBSERVED_CHANGED_OPTIONS: readonly ["canonizeResults", "context", "errorPolicy", "fetchPolicy", "refetchWritePolicy", "returnPartialData"]; + +// Warning: (ae-forgotten-export) The symbol "OBSERVED_CHANGED_OPTIONS" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type ObservedOptions = Pick; + +// @public (undocumented) +type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; + +// @public (undocumented) +interface Operation { + // (undocumented) + extensions: Record; + // (undocumented) + getContext: () => DefaultContext; + // (undocumented) + operationName: string; + // (undocumented) + query: DocumentNode; + // (undocumented) + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; + // (undocumented) + variables: Record; +} + +// @public (undocumented) +type OperationVariables = Record; + +// @public (undocumented) +type Path = ReadonlyArray; + +// @public (undocumented) +interface PendingPromise extends Promise { + // (undocumented) + status: "pending"; +} + +// @public +export interface PreloadedQueryRef extends QueryRef { + toPromise(): Promise>; +} + +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + +// @public (undocumented) +const PROMISE_SYMBOL: unique symbol; + +// Warning: (ae-forgotten-export) The symbol "PendingPromise" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FulfilledPromise" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RejectedPromise" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type PromiseWithState = PendingPromise | FulfilledPromise | RejectedPromise; + +// @public (undocumented) +const QUERY_REF_BRAND: unique symbol; + +// @public (undocumented) +const QUERY_REFERENCE_SYMBOL: unique symbol; + +// Warning: (ae-forgotten-export) The symbol "BaseQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) + defaultOptions?: Partial>; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; + skip?: boolean; +} + +// Warning: (ae-forgotten-export) The symbol "QueryFunctionOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface QueryHookOptions extends QueryFunctionOptions { +} + +// @public (undocumented) +class QueryInfo { + constructor(queryManager: QueryManager, queryId?: string); + // (undocumented) + document: DocumentNode | null; + // (undocumented) + getDiff(): Cache_2.DiffResult; + // (undocumented) + graphQLErrors?: ReadonlyArray; + // (undocumented) + init(query: { + document: DocumentNode; + variables: Record | undefined; + networkStatus?: NetworkStatus; + observableQuery?: ObservableQuery; + lastRequestId?: number; + }): this; + // (undocumented) + lastRequestId: number; + // Warning: (ae-forgotten-export) The symbol "QueryListener" needs to be exported by the entry point index.d.ts + // + // (undocumented) + listeners: Set; + // (undocumented) + markError(error: ApolloError): ApolloError; + // (undocumented) + markReady(): NetworkStatus; + // Warning: (ae-forgotten-export) The symbol "CacheWriteBehavior" needs to be exported by the entry point index.d.ts + // + // (undocumented) + markResult(result: FetchResult, document: DocumentNode, options: Pick, cacheWriteBehavior: CacheWriteBehavior): void; + // (undocumented) + networkError?: Error | null; + // (undocumented) + networkStatus?: NetworkStatus; + // (undocumented) + notify(): void; + // (undocumented) + readonly observableQuery: ObservableQuery | null; + // (undocumented) + readonly queryId: string; + // (undocumented) + reset(): void; + // (undocumented) + resetDiff(): void; + // (undocumented) + resetLastWrite(): void; + // (undocumented) + setDiff(diff: Cache_2.DiffResult | null): void; + // (undocumented) + setObservableQuery(oq: ObservableQuery | null): void; + // (undocumented) + stop(): void; + // (undocumented) + stopped: boolean; + // (undocumented) + variables?: Record; +} + +// @public (undocumented) +export interface QueryKey { + // (undocumented) + __queryKey?: string; +} + +// @public (undocumented) +type QueryListener = (queryInfo: QueryInfo) => void; + +// @public (undocumented) +class QueryManager { + constructor({ cache, link, defaultOptions, documentTransform, queryDeduplication, onBroadcast, ssrMode, clientAwareness, localState, assumeImmutableResults, defaultContext, }: { + cache: ApolloCache; + link: ApolloLink; + defaultOptions?: DefaultOptions; + documentTransform?: DocumentTransform; + queryDeduplication?: boolean; + onBroadcast?: () => void; + ssrMode?: boolean; + clientAwareness?: Record; + localState?: LocalState; + assumeImmutableResults?: boolean; + defaultContext?: Partial; + }); + // (undocumented) + readonly assumeImmutableResults: boolean; + // (undocumented) + broadcastQueries(): void; + // (undocumented) + 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) + defaultOptions: DefaultOptions; + // (undocumented) + readonly documentTransform: DocumentTransform; + // (undocumented) + protected fetchCancelFns: Map any>; + // (undocumented) + fetchQuery(queryId: string, options: WatchQueryOptions, networkStatus?: NetworkStatus): Promise>; + // (undocumented) + generateMutationId(): string; + // (undocumented) + generateQueryId(): string; + // (undocumented) + generateRequestId(): number; + // Warning: (ae-forgotten-export) The symbol "TransformCacheEntry" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getDocumentInfo(document: DocumentNode): TransformCacheEntry; + // (undocumented) + getLocalState(): LocalState; + // (undocumented) + getObservableQueries(include?: InternalRefetchQueriesInclude): Map>; + // Warning: (ae-forgotten-export) The symbol "QueryStoreValue" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getQueryStore(): Record; + // (undocumented) + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; + // (undocumented) + link: ApolloLink; + // (undocumented) + markMutationOptimistic>(optimisticResponse: any, mutation: { + mutationId: string; + document: DocumentNode; + variables?: TVariables; + fetchPolicy?: MutationFetchPolicy; + errorPolicy: ErrorPolicy; + context?: TContext; + updateQueries: UpdateQueries; + update?: MutationUpdaterFunction; + keepRootFields?: boolean; + }): boolean; + // (undocumented) + markMutationResult>(mutation: { + mutationId: string; + result: FetchResult; + document: DocumentNode; + variables?: TVariables; + fetchPolicy?: MutationFetchPolicy; + errorPolicy: ErrorPolicy; + context?: TContext; + updateQueries: UpdateQueries; + update?: MutationUpdaterFunction; + awaitRefetchQueries?: boolean; + refetchQueries?: InternalRefetchQueriesInclude; + removeOptimistic?: string; + onQueryUpdated?: OnQueryUpdated; + keepRootFields?: boolean; + }, cache?: ApolloCache): Promise>; + // (undocumented) + mutate, TCache extends ApolloCache>({ mutation, variables, optimisticResponse, updateQueries, refetchQueries, awaitRefetchQueries, update: updateWithProxyFn, onQueryUpdated, fetchPolicy, errorPolicy, keepRootFields, context, }: MutationOptions): Promise>; + // (undocumented) + mutationStore?: { + [mutationId: string]: MutationStoreValue; + }; + // (undocumented) + query(options: QueryOptions, queryId?: string): Promise>; + // (undocumented) + reFetchObservableQueries(includeStandby?: boolean): Promise[]>; + // Warning: (ae-forgotten-export) The symbol "InternalRefetchQueriesOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "InternalRefetchQueriesMap" needs to be exported by the entry point index.d.ts + // + // (undocumented) + refetchQueries({ updateCache, include, optimistic, removeOptimistic, onQueryUpdated, }: InternalRefetchQueriesOptions, TResult>): InternalRefetchQueriesMap; + // (undocumented) + removeQuery(queryId: string): void; + // (undocumented) + resetErrors(queryId: string): void; + // (undocumented) + setObservableQuery(observableQuery: ObservableQuery): void; + // (undocumented) + readonly ssrMode: boolean; + // (undocumented) + startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; + stop(): void; + // (undocumented) + stopQuery(queryId: string): void; + // (undocumented) + stopQueryInStore(queryId: string): void; + // (undocumented) + transform(document: DocumentNode): DocumentNode; + // (undocumented) + watchQuery(options: WatchQueryOptions): ObservableQuery; +} + +// @public +interface QueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: FetchPolicy; + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + query: DocumentNode | TypedDocumentNode; + returnPartialData?: boolean; + variables?: TVariables; +} + +// @public +export interface QueryRef { + // @internal (undocumented) + [QUERY_REF_BRAND]?(variables: TVariables): TData; +} + +// @public @deprecated (undocumented) +export interface QueryReference extends QueryRef { + // @deprecated (undocumented) + toPromise?: unknown; +} + +// Warning: (ae-forgotten-export) The symbol "PromiseWithState" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type QueryRefPromise = PromiseWithState>; + +// Warning: (ae-forgotten-export) The symbol "ObservableQueryFields" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface QueryResult extends ObservableQueryFields { + called: boolean; + client: ApolloClient; + data: TData | undefined; + error?: ApolloError; + loading: boolean; + networkStatus: NetworkStatus; + observable: ObservableQuery; + previousData?: TData; +} + +// @public (undocumented) +type QueryStoreValue = Pick; + +// @public (undocumented) +interface ReadFieldFunction { + // Warning: (ae-forgotten-export) The symbol "ReadFieldOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "SafeReadonly" needs to be exported by the entry point index.d.ts + // + // (undocumented) + (options: ReadFieldOptions): SafeReadonly | undefined; + // (undocumented) + (fieldName: string, from?: StoreObject | Reference): SafeReadonly | undefined; +} + +// Warning: (ae-forgotten-export) The symbol "FieldSpecifier" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface ReadFieldOptions extends FieldSpecifier { + // (undocumented) + from?: StoreObject | Reference; +} + +// @public (undocumented) +interface Reference { + // (undocumented) + readonly __ref: string; +} + +// @public (undocumented) +type RefetchFunction = ObservableQueryFields["refetch"]; + +// @public (undocumented) +type RefetchQueriesInclude = RefetchQueryDescriptor[] | RefetchQueriesIncludeShorthand; + +// @public (undocumented) +type RefetchQueriesIncludeShorthand = "all" | "active"; + +// @public (undocumented) +interface RefetchQueriesOptions, TResult> { + // (undocumented) + include?: RefetchQueriesInclude; + // (undocumented) + onQueryUpdated?: OnQueryUpdated | null; + // (undocumented) + optimistic?: boolean; + // (undocumented) + updateCache?: (cache: TCache) => void; +} + +// Warning: (ae-forgotten-export) The symbol "IsStrictlyAny" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type RefetchQueriesPromiseResults = IsStrictlyAny extends true ? any[] : TResult extends boolean ? ApolloQueryResult[] : TResult extends PromiseLike ? U[] : TResult[]; + +// Warning: (ae-forgotten-export) The symbol "RefetchQueriesPromiseResults" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface RefetchQueriesResult extends Promise> { + // (undocumented) + queries: ObservableQuery[]; + // (undocumented) + results: InternalRefetchQueriesResult[]; +} + +// @public (undocumented) +type RefetchQueryDescriptor = string | DocumentNode; + +// @public (undocumented) +type RefetchWritePolicy = "merge" | "overwrite"; + +// @public (undocumented) +interface RejectedPromise extends Promise { + // (undocumented) + reason: unknown; + // (undocumented) + status: "rejected"; +} + +// @public (undocumented) +type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; + +// @public (undocumented) +type Resolver = (rootValue?: any, args?: any, context?: any, info?: { + field: FieldNode; + fragmentMap: FragmentMap; +}) => any; + +// @public (undocumented) +interface Resolvers { + // (undocumented) + [key: string]: { + [field: string]: Resolver; + }; +} + +// @public (undocumented) +type SafeReadonly = T extends object ? Readonly : T; + +// @public (undocumented) +type ServerError = Error & { + response: Response; + result: Record | string; + statusCode: number; +}; + +// @public (undocumented) +type ServerParseError = Error & { + response: Response; + statusCode: number; + bodyText: string; +}; + +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + +// @public (undocumented) +interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { + // (undocumented) + context?: TContext; + // (undocumented) + data?: TData | null; +} + +// @public (undocumented) +type SkipToken = typeof skipToken; + +// @public (undocumented) +const skipToken: unique symbol; + +// @public (undocumented) +type Source = MaybeAsync>; + +// @public (undocumented) +type StorageType = Record; + +// @public (undocumented) +interface StoreObject { + // (undocumented) + [storeFieldName: string]: StoreValue; + // (undocumented) + __typename?: string; +} + +// Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; + +// @public (undocumented) +type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; + +// @public (undocumented) +type SubscribeToMoreFunction = ObservableQueryFields["subscribeToMore"]; + +// @public (undocumented) +type SubscribeToMoreOptions = { + document: DocumentNode | TypedDocumentNode; + variables?: TSubscriptionVariables; + updateQuery?: UpdateQueryFn; + onError?: (error: Error) => void; + context?: DefaultContext; +}; + +// @public (undocumented) +interface SubscriptionOptions { + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: FetchPolicy; + query: DocumentNode | TypedDocumentNode; + variables?: TVariables; +} + +// @public (undocumented) +class SuspenseCache { + constructor(options?: SuspenseCacheOptions); + // (undocumented) + add(cacheKey: CacheKey, queryRef: InternalQueryReference): void; + // (undocumented) + getQueryRef(cacheKey: CacheKey, createObservable: () => ObservableQuery): InternalQueryReference; +} + +// @public (undocumented) +export interface SuspenseCacheOptions { + autoDisposeTimeoutMs?: number; +} + +// @public (undocumented) +const suspenseCacheSymbol: unique symbol; + +// @public (undocumented) +type SuspenseQueryHookFetchPolicy = Extract; + +// @public (undocumented) +interface SuspenseQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + // Warning: (ae-forgotten-export) The symbol "SuspenseQueryHookFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: SuspenseQueryHookFetchPolicy; + queryKey?: string | number | any[]; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + // @deprecated + skip?: boolean; + variables?: TVariables; +} + +// @public (undocumented) +type ToReferenceFunction = (objOrIdOrRef: StoreObject | string | Reference, mergeIntoStore?: boolean) => Reference | undefined; + +// @public (undocumented) +type Transaction = (c: ApolloCache) => void; + +// @public (undocumented) +interface TransformCacheEntry { + // (undocumented) + asQuery: DocumentNode; + // (undocumented) + clientQuery: DocumentNode | null; + // (undocumented) + defaultVars: OperationVariables; + // (undocumented) + hasClientExports: boolean; + // (undocumented) + hasForcedResolvers: boolean; + // (undocumented) + hasNonreactiveDirective: boolean; + // (undocumented) + serverQuery: DocumentNode | null; +} + +// @public (undocumented) +type TransformFn = (document: DocumentNode) => DocumentNode; + +// @public (undocumented) +type UnionForAny = T extends never ? "a" : 1; + +// @public (undocumented) +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; + +// @public (undocumented) +export function unwrapQueryRef(queryRef: WrappedQueryRef): InternalQueryReference; + +// @public (undocumented) +export function unwrapQueryRef(queryRef: Partial>): undefined | InternalQueryReference; + +// @public (undocumented) +type UpdateQueries = MutationOptions["updateQueries"]; + +// @public (undocumented) +type UpdateQueryFn = (previousQueryResult: TData, options: { + subscriptionData: { + data: TSubscriptionData; + }; + variables?: TSubscriptionVariables; +}) => TData; + +// @public (undocumented) +export function updateWrappedQueryRef(queryRef: WrappedQueryRef, promise: QueryRefPromise): void; + +// @public (undocumented) +interface UriFunction { + // (undocumented) + (operation: Operation): string; +} + +// Warning: (ae-forgotten-export) The symbol "BackgroundQueryHookOptionsNoInfer" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UseBackgroundQueryResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function useBackgroundQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer & TOptions): [ +(QueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables> | (TOptions["skip"] extends boolean ? undefined : never)), +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; +}): [ +QueryRef | undefined, TVariables>, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { + errorPolicy: "ignore" | "all"; +}): [ +QueryRef, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { + skip: boolean; + returnPartialData: true; +}): [ +QueryRef, TVariables> | undefined, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { + returnPartialData: true; +}): [ +QueryRef, TVariables>, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { + skip: boolean; +}): [ +QueryRef | undefined, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryRef, UseBackgroundQueryResult]; + +// Warning: (ae-forgotten-export) The symbol "SkipToken" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken): [undefined, UseBackgroundQueryResult]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (BackgroundQueryHookOptionsNoInfer & { + returnPartialData: true; +})): [ +QueryRef, TVariables> | undefined, +UseBackgroundQueryResult +]; + +// @public (undocumented) +function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | BackgroundQueryHookOptionsNoInfer): [ +QueryRef | undefined, +UseBackgroundQueryResult +]; + +// @public (undocumented) +type UseBackgroundQueryResult = { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; +}; + +// Warning: (ae-forgotten-export) The symbol "UseFragmentOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UseFragmentResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function useFragment(options: UseFragmentOptions): UseFragmentResult; + +// @public (undocumented) +interface UseFragmentOptions extends Omit, NoInfer_2>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { + client?: ApolloClient; + // (undocumented) + from: StoreObject | Reference | string; + // (undocumented) + optimistic?: boolean; +} + +// @public (undocumented) +type UseFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing?: MissingTree; +}; + +// Warning: (ae-forgotten-export) The symbol "QueryResult" needs to be exported by the entry point index.d.ts +// +// @public +function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer_2>): QueryResult; + +// Warning: (ae-forgotten-export) The symbol "UseQueryRefHandlersResult" needs to be exported by the entry point index.d.ts +// +// @public +function useQueryRefHandlers(queryRef: QueryRef): UseQueryRefHandlersResult; + +// @public (undocumented) +interface UseQueryRefHandlersResult { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; +} + +// Warning: (ae-forgotten-export) The symbol "UseReadQueryResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function useReadQuery(queryRef: QueryRef): UseReadQueryResult; + +// @public (undocumented) +interface UseReadQueryResult { + data: TData; + error: ApolloError | undefined; + networkStatus: NetworkStatus; +} + +// Warning: (ae-forgotten-export) The symbol "SuspenseQueryHookOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UseSuspenseQueryResult" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; +}): UseSuspenseQueryResult | undefined, TVariables>; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { + errorPolicy: "ignore" | "all"; +}): UseSuspenseQueryResult; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { + skip: boolean; + returnPartialData: true; +}): UseSuspenseQueryResult | undefined, TVariables>; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { + returnPartialData: true; +}): UseSuspenseQueryResult, TVariables>; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { + skip: boolean; +}): UseSuspenseQueryResult; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer_2> & { + returnPartialData: true; +})): UseSuspenseQueryResult | undefined, TVariables>; + +// @public (undocumented) +function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; + +// @public (undocumented) +interface UseSuspenseQueryResult { + // (undocumented) + client: ApolloClient; + // (undocumented) + data: TData; + // (undocumented) + error: ApolloError | undefined; + // (undocumented) + fetchMore: FetchMoreFunction; + // (undocumented) + networkStatus: NetworkStatus; + // (undocumented) + refetch: RefetchFunction; + // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreFunction" needs to be exported by the entry point index.d.ts + // + // (undocumented) + subscribeToMore: SubscribeToMoreFunction; +} + +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + +// @public (undocumented) +type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; + +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; +} + +// @public (undocumented) +interface WrappableHooks { + // Warning: (ae-forgotten-export) The symbol "useBackgroundQuery" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useBackgroundQuery: typeof useBackgroundQuery; + // Warning: (ae-forgotten-export) The symbol "useFragment" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useFragment: typeof useFragment; + // Warning: (ae-forgotten-export) The symbol "useQuery" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useQuery: typeof useQuery; + // Warning: (ae-forgotten-export) The symbol "useQueryRefHandlers" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useQueryRefHandlers: typeof useQueryRefHandlers; + // Warning: (ae-forgotten-export) The symbol "useReadQuery" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useReadQuery: typeof useReadQuery; + // Warning: (ae-forgotten-export) The symbol "useSuspenseQuery" needs to be exported by the entry point index.d.ts + // + // (undocumented) + useSuspenseQuery: typeof useSuspenseQuery; +} + +// @internal +interface WrappedQueryRef extends QueryRef { + // (undocumented) + [PROMISE_SYMBOL]: QueryRefPromise; + // (undocumented) + readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; + // (undocumented) + toPromise?(): Promise; +} + +// @public (undocumented) +export function wrapQueryRef(internalQueryRef: InternalQueryReference): WrappedQueryRef; + +// Warnings were encountered during analysis: +// +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts +// 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:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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/react/hooks/useBackgroundQuery.ts:29:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useBackgroundQuery.ts:30:3 - (ae-forgotten-export) The symbol "RefetchFunction" 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_parser.md b/.api-reports/api-report-react_parser.md index 96d961506af..9dd7eab4a8b 100644 --- a/.api-reports/api-report-react_parser.md +++ b/.api-reports/api-report-react_parser.md @@ -34,6 +34,12 @@ export function operationName(type: DocumentType_2): string; // @public (undocumented) export function parser(document: DocumentNode): IDocumentDefinition; +// @public (undocumented) +export namespace parser { + var // (undocumented) + resetCache: () => void; +} + // @public (undocumented) export function verifyDocumentType(document: DocumentNode, type: DocumentType_2): void; diff --git a/.api-reports/api-report-react_ssr.md b/.api-reports/api-report-react_ssr.md index 5b11d65622a..e064686bc36 100644 --- a/.api-reports/api-report-react_ssr.md +++ b/.api-reports/api-report-react_ssr.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import type { DocumentNode } from 'graphql'; import type { ExecutionResult } from 'graphql'; @@ -15,11 +13,10 @@ import type { GraphQLError } from 'graphql'; import type { GraphQLErrorExtensions } from 'graphql'; import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import * as React_2 from 'react'; -import type { ReactElement } from 'react'; -import { ReactNode } from 'react'; +import type * as ReactTypes from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -40,10 +37,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -68,7 +68,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -80,6 +79,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -90,7 +93,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -100,30 +103,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -132,48 +130,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -181,38 +159,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -276,12 +258,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -293,31 +281,36 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { [K in keyof T]: T[K]; }; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -interface BaseQueryOptions extends Omit, "query"> { +interface BaseQueryOptions extends SharedWatchQueryOptions { // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts - // - // (undocumented) client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } @@ -326,7 +319,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -366,7 +359,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -447,7 +440,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -468,44 +461,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -525,11 +507,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -539,18 +518,48 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -582,14 +591,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -599,13 +611,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -652,13 +663,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -681,7 +690,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -690,8 +699,50 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) -export function getDataFromTree(tree: React_2.ReactNode, context?: { +export function getDataFromTree(tree: ReactTypes.ReactNode, context?: { [key: string]: any; }): Promise; @@ -702,11 +753,11 @@ export function getMarkupFromTree({ tree, context, renderFunction, }: GetMarkupF // @public (undocumented) type GetMarkupFromTreeOptions = { - tree: React_2.ReactNode; + tree: ReactTypes.ReactNode; context?: { [key: string]: any; }; - renderFunction?: (tree: React_2.ReactElement) => string | PromiseLike; + renderFunction?: (tree: ReactTypes.ReactElement) => string | PromiseLike; }; // @public (undocumented) @@ -726,6 +777,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -794,15 +854,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -888,31 +946,20 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -921,17 +968,10 @@ interface MutationBaseOptions; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -949,6 +989,15 @@ type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -967,21 +1016,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1013,8 +1055,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1039,7 +1079,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1047,6 +1086,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1059,26 +1100,34 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; +interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} // @public (undocumented) type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1094,7 +1143,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1105,6 +1157,9 @@ type OperationVariables = Record; // @public (undocumented) type Path = ReadonlyArray; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) interface QueryData { // (undocumented) @@ -1120,22 +1175,18 @@ interface QueryDataOptions) => ReactNode; - // (undocumented) + children?: (result: QueryResult) => ReactTypes.ReactNode; query: DocumentNode | TypedDocumentNode; } // Warning: (ae-forgotten-export) The symbol "BaseQueryOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface QueryFunctionOptions extends BaseQueryOptions { - // (undocumented) +interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -1153,7 +1204,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1177,17 +1228,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1201,7 +1254,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 +1265,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1221,6 +1275,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) @@ -1250,7 +1306,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1264,7 +1322,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1307,7 +1365,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1319,27 +1376,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1347,21 +1396,13 @@ interface QueryOptions { // // @public (undocumented) interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } @@ -1435,11 +1476,11 @@ type RefetchWritePolicy = "merge" | "overwrite"; // @public (undocumented) export class RenderPromises { // (undocumented) - addObservableQueryPromise(obsQuery: ObservableQuery): ReactNode; + addObservableQueryPromise(obsQuery: ObservableQuery): ReactTypes.ReactNode; // Warning: (ae-forgotten-export) The symbol "QueryData" needs to be exported by the entry point index.d.ts // // (undocumented) - addQueryPromise(queryInstance: QueryData, finish?: () => React.ReactNode): React.ReactNode; + addQueryPromise(queryInstance: QueryData, finish?: () => ReactTypes.ReactNode): ReactTypes.ReactNode; // (undocumented) consumeAndAwaitPromises(): Promise; // Warning: (ae-forgotten-export) The symbol "QueryDataOptions" needs to be exported by the entry point index.d.ts @@ -1455,7 +1496,7 @@ export class RenderPromises { } // @public (undocumented) -export function renderToStringWithData(component: ReactElement): Promise; +export function renderToStringWithData(component: ReactTypes.ReactElement): Promise; // @public (undocumented) type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; @@ -1491,6 +1532,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1516,7 +1578,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1532,15 +1594,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -1594,46 +1651,54 @@ interface UriFunction { (operation: Operation): string; } +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-testing.md b/.api-reports/api-report-testing.md index bdbed73258e..770cfebaa05 100644 --- a/.api-reports/api-report-testing.md +++ b/.api-reports/api-report-testing.md @@ -16,6 +16,7 @@ import type { Observer } from 'zen-observable-ts'; import * as React_2 from 'react'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -36,10 +37,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -64,7 +68,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -76,6 +79,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -86,7 +93,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -96,30 +103,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -128,48 +130,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -177,38 +159,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -272,12 +258,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -289,16 +281,23 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { @@ -310,7 +309,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -350,7 +349,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -431,7 +430,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -441,6 +440,11 @@ class Concast extends Observable { // @public (undocumented) type ConcastSourcesIterable = Iterable>; +// @internal (undocumented) +type CovariantUnaryFunction = { + fn(arg: Arg): Ret; +}["fn"]; + // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "NormalizedCacheObject" needs to be exported by the entry point index.d.ts // @@ -458,44 +462,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -515,11 +508,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -529,18 +519,48 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -572,14 +592,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -589,13 +612,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -642,13 +664,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -671,7 +691,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -680,6 +700,48 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -697,6 +759,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -761,7 +832,7 @@ function isReference(obj: any): obj is Reference; type IsStrictlyAny = UnionToIntersection> extends never ? true : false; // @public (undocumented) -export const itAsync: ((message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number | undefined) => any) & { +export const itAsync: ((this: unknown, message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number | undefined) => void) & { only: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; skip: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; todo: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; @@ -772,15 +843,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -868,7 +937,6 @@ export interface MockedProviderProps { childProps?: object; // (undocumented) children?: any; - // (undocumented) connectToDevTools?: boolean; // Warning: (ae-forgotten-export) The symbol "DefaultOptions" needs to be exported by the entry point index.d.ts // @@ -877,7 +945,7 @@ export interface MockedProviderProps { // (undocumented) link?: ApolloLink; // (undocumented) - mocks?: ReadonlyArray; + mocks?: ReadonlyArray>; // (undocumented) resolvers?: Resolvers; // (undocumented) @@ -891,17 +959,23 @@ interface MockedProviderState { } // @public (undocumented) -export interface MockedResponse, TVariables = Record> { +export interface MockedResponse, out TVariables = Record> { // (undocumented) delay?: number; // (undocumented) error?: Error; // (undocumented) - newData?: ResultFunction; + maxUsageCount?: number; + // (undocumented) + newData?: ResultFunction, TVariables>; // (undocumented) request: GraphQLRequest; // (undocumented) - result?: FetchResult | ResultFunction>; + result?: FetchResult | ResultFunction, TVariables>; + // Warning: (ae-forgotten-export) The symbol "VariableMatcher" needs to be exported by the entry point index.d.ts + // + // (undocumented) + variableMatcher?: VariableMatcher; } // @public (undocumented) @@ -916,7 +990,7 @@ interface MockedSubscriptionResult { // @public (undocumented) export class MockLink extends ApolloLink { - constructor(mockedResponses: ReadonlyArray, addTypename?: Boolean, options?: MockLinkOptions); + constructor(mockedResponses: ReadonlyArray>, addTypename?: Boolean, options?: MockLinkOptions); // (undocumented) addMockedResponse(mockedResponse: MockedResponse): void; // (undocumented) @@ -951,7 +1025,7 @@ export class MockSubscriptionLink extends ApolloLink { // (undocumented) onUnsubscribe(listener: any): void; // (undocumented) - operation: Operation; + operation?: Operation; // (undocumented) request(operation: Operation): Observable; // (undocumented) @@ -993,31 +1067,20 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -1026,17 +1089,10 @@ interface MutationBaseOptions; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1054,6 +1110,15 @@ type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1072,21 +1137,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1110,7 +1168,7 @@ type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) +// @public interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -1128,8 +1186,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1154,7 +1210,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1162,6 +1217,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1174,21 +1231,14 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } @@ -1206,7 +1256,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1217,6 +1270,9 @@ type OperationVariables = Record; // @public (undocumented) type Path = ReadonlyArray; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) class QueryInfo { constructor(queryManager: QueryManager, queryId?: string); @@ -1231,7 +1287,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1255,17 +1311,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1279,7 +1337,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; @@ -1290,6 +1348,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1300,6 +1359,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -1326,7 +1387,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1340,7 +1403,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1383,7 +1446,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1395,27 +1457,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1503,8 +1557,10 @@ interface Resolvers { }; } +// Warning: (ae-forgotten-export) The symbol "CovariantUnaryFunction" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export type ResultFunction = () => T; +export type ResultFunction> = CovariantUnaryFunction; // @public (undocumented) type SafeReadonly = T extends object ? Readonly : T; @@ -1523,6 +1579,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1548,7 +1625,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1567,15 +1644,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -1632,58 +1704,71 @@ interface UriFunction { (operation: Operation): string; } +// @public (undocumented) +type VariableMatcher> = CovariantUnaryFunction; + // @public (undocumented) export function wait(ms: number): Promise; +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } -// @public (undocumented) +// @public @deprecated (undocumented) export function withErrorSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; -// @public (undocumented) +// @public @deprecated (undocumented) export function withLogSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; -// @public (undocumented) +// @public @deprecated (undocumented) export function withWarningSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-testing_core.md b/.api-reports/api-report-testing_core.md index f5de976f0c6..22938c92435 100644 --- a/.api-reports/api-report-testing_core.md +++ b/.api-reports/api-report-testing_core.md @@ -15,6 +15,7 @@ import { Observable } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; import type { Subscriber } from 'zen-observable-ts'; import type { Subscription } from 'zen-observable-ts'; +import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; // Warning: (ae-forgotten-export) The symbol "Modifier" needs to be exported by the entry point index.d.ts @@ -35,10 +36,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // Warning: (ae-forgotten-export) The symbol "StoreObject" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -63,7 +67,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -75,6 +78,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "Reference" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -85,7 +92,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -95,30 +102,25 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; // Warning: (ae-forgotten-export) The symbol "DocumentTransform" needs to be exported by the entry point index.d.ts - // - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -127,48 +129,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -176,38 +158,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -271,12 +257,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -288,16 +280,23 @@ class ApolloLink { } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} -// @public (undocumented) +// @public type AsStoreObject = { @@ -309,7 +308,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -349,7 +348,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -430,7 +429,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -440,6 +439,11 @@ class Concast extends Observable { // @public (undocumented) type ConcastSourcesIterable = Iterable>; +// @internal (undocumented) +type CovariantUnaryFunction = { + fn(arg: Arg): Ret; +}["fn"]; + // Warning: (ae-forgotten-export) The symbol "ApolloClient" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "NormalizedCacheObject" needs to be exported by the entry point index.d.ts // @@ -457,44 +461,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -514,11 +507,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -528,18 +518,48 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } +// Warning: (ae-forgotten-export) The symbol "DeepPartialPrimitive" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlyMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialSet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialReadonlySet" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; + +// Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialMap = {} & Map, DeepPartial>; + +// @public (undocumented) +type DeepPartialObject = { + [K in keyof T]?: DeepPartial; +}; + +// Warning: (ae-forgotten-export) The symbol "Primitive" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type DeepPartialPrimitive = Primitive | Date | RegExp; + +// @public (undocumented) +type DeepPartialReadonlyMap = {} & ReadonlyMap, DeepPartial>; + +// @public (undocumented) +type DeepPartialReadonlySet = {} & ReadonlySet>; + +// @public (undocumented) +type DeepPartialSet = {} & Set>; + // @public (undocumented) interface DefaultContext extends Record { } @@ -571,14 +591,17 @@ class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -588,13 +611,12 @@ type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -641,13 +663,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // Warning: (ae-forgotten-export) The symbol "SingleExecutionResult" needs to be exported by the entry point index.d.ts @@ -670,7 +690,7 @@ interface FieldSpecifier { variables?: Record; } -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -679,6 +699,48 @@ interface FragmentMap { // @public (undocumented) type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) type GraphQLErrors = ReadonlyArray; @@ -696,6 +758,15 @@ interface GraphQLRequest> { variables?: TVariables; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) interface IncrementalPayload { // (undocumented) @@ -760,7 +831,7 @@ function isReference(obj: any): obj is Reference; type IsStrictlyAny = UnionToIntersection> extends never ? true : false; // @public (undocumented) -export const itAsync: ((message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number | undefined) => any) & { +export const itAsync: ((this: unknown, message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number | undefined) => void) & { only: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; skip: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; todo: (message: string, callback: (resolve: (result?: any) => void, reject: (reason?: any) => void) => any, timeout?: number) => void; @@ -771,15 +842,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -845,17 +914,23 @@ interface MockApolloLink extends ApolloLink { } // @public (undocumented) -export interface MockedResponse, TVariables = Record> { +export interface MockedResponse, out TVariables = Record> { // (undocumented) delay?: number; // (undocumented) error?: Error; // (undocumented) - newData?: ResultFunction; + maxUsageCount?: number; + // (undocumented) + newData?: ResultFunction, TVariables>; // (undocumented) request: GraphQLRequest; // (undocumented) - result?: FetchResult | ResultFunction>; + result?: FetchResult | ResultFunction, TVariables>; + // Warning: (ae-forgotten-export) The symbol "VariableMatcher" needs to be exported by the entry point index.d.ts + // + // (undocumented) + variableMatcher?: VariableMatcher; } // @public (undocumented) @@ -870,7 +945,7 @@ interface MockedSubscriptionResult { // @public (undocumented) export class MockLink extends ApolloLink { - constructor(mockedResponses: ReadonlyArray, addTypename?: Boolean, options?: MockLinkOptions); + constructor(mockedResponses: ReadonlyArray>, addTypename?: Boolean, options?: MockLinkOptions); // (undocumented) addMockedResponse(mockedResponse: MockedResponse): void; // (undocumented) @@ -905,7 +980,7 @@ export class MockSubscriptionLink extends ApolloLink { // (undocumented) onUnsubscribe(listener: any): void; // (undocumented) - operation: Operation; + operation?: Operation; // (undocumented) request(operation: Operation): Observable; // (undocumented) @@ -947,31 +1022,20 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -980,17 +1044,10 @@ interface MutationBaseOptions; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1008,6 +1065,15 @@ type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1026,21 +1092,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1064,7 +1123,7 @@ type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) +// @public interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -1082,8 +1141,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1108,7 +1165,6 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1116,6 +1172,8 @@ class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1128,21 +1186,14 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } @@ -1160,7 +1211,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1171,6 +1225,9 @@ type OperationVariables = Record; // @public (undocumented) type Path = ReadonlyArray; +// @public (undocumented) +type Primitive = null | undefined | string | number | boolean | symbol | bigint; + // @public (undocumented) class QueryInfo { constructor(queryManager: QueryManager, queryId?: string); @@ -1185,7 +1242,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1209,17 +1266,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1233,7 +1292,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; @@ -1244,6 +1303,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1253,6 +1313,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) @@ -1282,7 +1344,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -1296,7 +1360,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -1339,7 +1403,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -1351,27 +1414,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -1459,8 +1514,10 @@ interface Resolvers { }; } +// Warning: (ae-forgotten-export) The symbol "CovariantUnaryFunction" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export type ResultFunction = () => T; +export type ResultFunction> = CovariantUnaryFunction; // @public (undocumented) type SafeReadonly = T extends object ? Readonly : T; @@ -1479,6 +1536,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -1504,7 +1582,7 @@ interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -1523,15 +1601,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -1588,58 +1661,71 @@ interface UriFunction { (operation: Operation): string; } +// @public (undocumented) +type VariableMatcher> = CovariantUnaryFunction; + // @public (undocumented) export function wait(ms: number): Promise; +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } -// @public (undocumented) +// @public @deprecated (undocumented) export function withErrorSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; -// @public (undocumented) +// @public @deprecated (undocumented) export function withLogSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; -// @public (undocumented) +// @public @deprecated (undocumented) export function withWarningSpy(it: (...args: TArgs) => TResult, ...args: TArgs): TResult; // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:96:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:97:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:98:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:99:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts -// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:100:3 - (ae-forgotten-export) The symbol "ReadFieldFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:101:3 - (ae-forgotten-export) The symbol "CanReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:102:3 - (ae-forgotten-export) The symbol "isReference" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:103:3 - (ae-forgotten-export) The symbol "ToReferenceFunction" needs to be exported by the entry point index.d.ts +// src/cache/core/types/common.ts:104:3 - (ae-forgotten-export) The symbol "StorageType" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/DocumentTransform.ts:122:7 - (ae-forgotten-export) The symbol "DocumentTransformCacheKey" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 // (No @packageDocumentation comment for this package) diff --git a/.api-reports/api-report-testing_experimental.md b/.api-reports/api-report-testing_experimental.md new file mode 100644 index 00000000000..caeebb6e726 --- /dev/null +++ b/.api-reports/api-report-testing_experimental.md @@ -0,0 +1,89 @@ +## API Report File for "@apollo/client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +import type { FieldNode } from 'graphql'; +import type { FragmentDefinitionNode } from 'graphql'; +import type { GraphQLSchema } from 'graphql'; + +// @alpha +export const createSchemaFetch: (schema: GraphQLSchema, mockFetchOpts?: { + validate?: boolean; + delay?: { + min: number; + max: number; + }; +}) => ((uri?: any, options?: any) => Promise) & { + mockGlobal: () => { + restore: () => void; + } & Disposable; +}; + +// Warning: (ae-forgotten-export) The symbol "TestSchemaOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ProxiedSchema" needs to be exported by the entry point index.d.ts +// +// @alpha +export const createTestSchema: (schemaWithTypeDefs: GraphQLSchema, options: TestSchemaOptions) => ProxiedSchema; + +// @public +interface FragmentMap { + // (undocumented) + [fragmentName: string]: FragmentDefinitionNode; +} + +// Warning: (ae-forgotten-export) The symbol "TestSchemaFns" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +type ProxiedSchema = GraphQLSchema & TestSchemaFns; + +// @public (undocumented) +type Resolver = (rootValue?: any, args?: any, context?: any, info?: { + field: FieldNode; + fragmentMap: FragmentMap; +}) => any; + +// @public (undocumented) +interface Resolvers { + // (undocumented) + [key: string]: { + [field: string]: Resolver; + }; +} + +// @public (undocumented) +interface TestSchemaFns { + // (undocumented) + add: (addOptions: { + resolvers: Resolvers; + }) => ProxiedSchema; + // (undocumented) + fork: (forkOptions?: { + resolvers?: Resolvers; + }) => ProxiedSchema; + // (undocumented) + reset: () => void; +} + +// @public (undocumented) +interface TestSchemaOptions { + // (undocumented) + resolvers: Resolvers; + // (undocumented) + scalars?: { + [key: string]: any; + }; +} + +// Warnings were encountered during analysis: +// +// 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/types.ts:203:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts +// src/testing/experimental/createTestSchema.ts:10:23 - (ae-forgotten-export) The symbol "Resolvers" 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.md b/.api-reports/api-report-utilities.md index 742cf060348..49b55f0b20d 100644 --- a/.api-reports/api-report-utilities.md +++ b/.api-reports/api-report-utilities.md @@ -20,15 +20,16 @@ import { Observable } from 'zen-observable-ts'; import type { Subscription as ObservableSubscription } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; import type { OperationDefinitionNode } from 'graphql'; -import { print as print_3 } from 'graphql'; import type { SelectionNode } from 'graphql'; import type { SelectionSetNode } from 'graphql'; +import { StrongCache } from '@wry/caches'; import type { Subscriber } from 'zen-observable-ts'; import { Trie } from '@wry/trie'; import { TypedDocumentNode } from '@graphql-typed-document-node/core'; import type { ValueNode } from 'graphql'; import type { VariableDefinitionNode } from 'graphql'; import type { VariableNode } from 'graphql'; +import { WeakCache } from '@wry/caches'; // @public (undocumented) export const addTypenameToDocument: ((doc: TNode) => TNode) & { @@ -53,10 +54,13 @@ abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -79,7 +83,6 @@ abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -91,6 +94,10 @@ abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "WatchFragmentResult" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // (undocumented) abstract write(write: Cache_2.WriteOptions): Reference | undefined; // (undocumented) @@ -99,7 +106,7 @@ abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; @@ -109,28 +116,24 @@ class ApolloClient implements DataProxy { // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; // Warning: (ae-forgotten-export) The symbol "Resolvers" needs to be exported by the entry point index.d.ts - // - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // Warning: (ae-forgotten-export) The symbol "ApolloCache" needs to be exported by the entry point index.d.ts // // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesInclude" needs to be exported by the entry point index.d.ts - // - // (undocumented) getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // Warning: (ae-forgotten-export) The symbol "ApolloLink" needs to be exported by the entry point index.d.ts // @@ -139,48 +142,28 @@ class ApolloClient implements DataProxy { // Warning: (ae-forgotten-export) The symbol "DefaultContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "MutationOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "FetchResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; // Warning: (ae-forgotten-export) The symbol "QueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ApolloQueryResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; // Warning: (ae-forgotten-export) The symbol "RefetchQueriesOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "RefetchQueriesResult" needs to be exported by the entry point index.d.ts - // - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; // Warning: (ae-forgotten-export) The symbol "FragmentMatcher" needs to be exported by the entry point index.d.ts - // - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; // Warning: (ae-forgotten-export) The symbol "SubscriptionOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "ApolloClientOptions" needs to be exported by the entry point index.d.ts // @@ -188,38 +171,42 @@ class ApolloClient implements DataProxy { readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts + watchFragment(options: WatchFragmentOptions): Observable>; // Warning: (ae-forgotten-export) The symbol "WatchQueryOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ObservableQuery" needs to be exported by the entry point index.d.ts - // - // (undocumented) watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -type ApolloClientOptions = { - uri?: string | UriFunction; +interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + // Warning: (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // @public (undocumented) class ApolloError extends Error { @@ -283,12 +270,18 @@ class ApolloLink { // // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // Warning: (ae-forgotten-export) The symbol "NextLink" needs to be exported by the entry point index.d.ts // // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // Warning: (ae-forgotten-export) The symbol "Operation" needs to be exported by the entry point index.d.ts @@ -311,14 +304,21 @@ interface ApolloPayloadResult, TExtensions = Record< } // @public (undocumented) -type ApolloQueryResult = { +interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; + // Warning: (ae-forgotten-export) The symbol "ApolloError" needs to be exported by the entry point index.d.ts error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // Warning: (ae-forgotten-export) The symbol "NetworkStatus" needs to be exported by the entry point index.d.ts + // + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} // @public (undocumented) type ApolloReducerConfig = { @@ -329,7 +329,7 @@ type ApolloReducerConfig = { // @public (undocumented) export function argumentsObjectFromField(field: FieldNode | DirectiveNode, variables?: Record): Object | null; -// @public (undocumented) +// @public export type AsStoreObject = { @@ -339,6 +339,18 @@ export type AsStoreObject(observable: Observable, mapFn: (value: V) => R | PromiseLike, catchFn?: (error: any) => R | PromiseLike): Observable; +// @internal +export const AutoCleanedStrongCache: typeof StrongCache; + +// @internal (undocumented) +export type AutoCleanedStrongCache = StrongCache; + +// @internal +export const AutoCleanedWeakCache: typeof WeakCache; + +// @internal (undocumented) +export type AutoCleanedWeakCache = WeakCache; + // Warning: (ae-forgotten-export) The symbol "InMemoryCache" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -352,7 +364,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -392,7 +404,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -459,6 +471,27 @@ class CacheGroup { resetCaching(): void; } +// @public +export interface CacheSizes { + "cache.fragmentQueryDocuments": number; + "documentTransform.cache": number; + "fragmentRegistry.findFragmentSpreads": number; + "fragmentRegistry.lookup": number; + "fragmentRegistry.transform": number; + "inMemoryCache.executeSelectionSet": number; + "inMemoryCache.executeSubSelectedArray": number; + "inMemoryCache.maybeBroadcastWatch": number; + "PersistedQueryLink.persistedQueryHashes": number; + "queryManager.getDocumentInfo": number; + "removeTypenameFromVariables.getVariableDefinitions": number; + canonicalStringify: number; + parser: number; + print: number; +} + +// @public +export const cacheSizes: Partial; + // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -469,6 +502,11 @@ const enum CacheWriteBehavior { OVERWRITE = 1 } +// @public +export const canonicalStringify: ((value: any) => string) & { + reset(): void; +}; + // @public (undocumented) type CanReadFunction = (value: StoreValue) => boolean; @@ -493,10 +531,10 @@ export const canUseWeakSet: boolean; // @public (undocumented) export function checkDocument(doc: DocumentNode): DocumentNode; -// @public (undocumented) +// @public export function cloneDeep(value: T): T; -// @public (undocumented) +// @public export function compact(...objects: TArgs): TupleToIntersection; // @public (undocumented) @@ -512,7 +550,7 @@ export class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -555,44 +593,33 @@ namespace DataProxy { }; // (undocumented) interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts // // (undocumented) interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -612,11 +639,8 @@ namespace DataProxy { } // (undocumented) interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // Warning: (ae-forgotten-export) The symbol "DataProxy" needs to be exported by the entry point index.d.ts @@ -626,15 +650,11 @@ namespace DataProxy { } } -// @public (undocumented) +// @public interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } @@ -675,7 +695,7 @@ type DeepOmitPrimitive = Primitive | Function; // Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends ReadonlyArray ? TItem[] extends T ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; +export type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; // @public (undocumented) type DeepPartialMap = {} & Map, DeepPartial>; @@ -697,6 +717,38 @@ type DeepPartialReadonlySet = {} & ReadonlySet>; // @public (undocumented) type DeepPartialSet = {} & Set>; +// @public (undocumented) +export const enum defaultCacheSizes { + // (undocumented) + "cache.fragmentQueryDocuments" = 1000, + // (undocumented) + "documentTransform.cache" = 2000, + // (undocumented) + "fragmentRegistry.findFragmentSpreads" = 4000, + // (undocumented) + "fragmentRegistry.lookup" = 1000, + // (undocumented) + "fragmentRegistry.transform" = 2000, + // (undocumented) + "inMemoryCache.executeSelectionSet" = 50000, + // (undocumented) + "inMemoryCache.executeSubSelectedArray" = 10000, + // (undocumented) + "inMemoryCache.maybeBroadcastWatch" = 5000, + // (undocumented) + "PersistedQueryLink.persistedQueryHashes" = 2000, + // (undocumented) + "queryManager.getDocumentInfo" = 2000, + // (undocumented) + "removeTypenameFromVariables.getVariableDefinitions" = 2000, + // (undocumented) + canonicalStringify = 1000, + // (undocumented) + parser = 1000, + // (undocumented) + print = 2000 +} + // @public (undocumented) interface DefaultContext extends Record { } @@ -720,7 +772,7 @@ interface DeleteModifier { // @public (undocumented) const _deleteModifier: unique symbol; -// @public (undocumented) +// @public @deprecated (undocumented) export const DEV: boolean; // @public (undocumented) @@ -745,14 +797,17 @@ export class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -762,9 +817,7 @@ export type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } @@ -817,7 +870,10 @@ abstract class EntityStore implements NormalizedCache { has(dataId: string): boolean; // (undocumented) protected lookup(dataId: string, dependOnExistence?: boolean): StoreObject | undefined; - // (undocumented) + makeCacheKey(document: DocumentNode, callback: Cache_2.WatchCallback, details: string): object; + makeCacheKey(selectionSet: SelectionSetNode, parent: string | StoreObject, varString: string | undefined, canonizeResults: boolean): object; + makeCacheKey(field: FieldNode, array: readonly any[], varString: string | undefined): object; + // @deprecated (undocumented) makeCacheKey(...args: any[]): object; // (undocumented) merge(older: string | StoreObject, newer: StoreObject | string): void; @@ -867,7 +923,7 @@ namespace EntityStore { } } -// @public (undocumented) +// @public type ErrorPolicy = "none" | "ignore" | "all"; // Warning: (ae-forgotten-export) The symbol "ExecutionPatchResultBase" needs to be exported by the entry point index.d.ts @@ -914,13 +970,11 @@ interface ExecutionPatchResultBase { interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // @public (undocumented) @@ -1000,7 +1054,7 @@ export function fixObservableSubclass Observab // @public (undocumented) type FlavorableWriteContext = Pick; -// @public (undocumented) +// @public export interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -1019,6 +1073,8 @@ interface FragmentRegistryAPI { // (undocumented) register(...fragments: DocumentNode[]): this; // (undocumented) + resetCaches(): void; + // (undocumented) transform(document: D): D; } @@ -1030,6 +1086,48 @@ interface FulfilledPromise extends Promise { value: TValue; } +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) export function getDefaultValues(definition: OperationDefinitionNode | undefined): Record; @@ -1048,7 +1146,7 @@ export function getFragmentDefinitions(doc: DocumentNode): FragmentDefinitionNod // @public (undocumented) export function getFragmentFromSelection(selection: SelectionNode, fragmentMap?: FragmentMap | FragmentMapFunction): InlineFragmentNode | FragmentDefinitionNode | null; -// @public (undocumented) +// @public export function getFragmentQueryDocument(document: DocumentNode, fragmentName?: string): DocumentNode; // @public (undocumented) @@ -1060,7 +1158,27 @@ export function getGraphQLErrorsFromResult(result: FetchResult): GraphQLEr // @public (undocumented) export function getInclusionDirectives(directives: ReadonlyArray): InclusionDirectives; -// @public (undocumented) +// @internal +const getInMemoryCacheMemoryInternals: (() => { + addTypenameDocumentTransform: { + cache: number; + }[]; + inMemoryCache: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + }; + fragmentRegistry: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + }; + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @public export function getMainDefinition(queryDoc: DocumentNode): OperationDefinitionNode | FragmentDefinitionNode; // @public (undocumented) @@ -1080,7 +1198,7 @@ export function getQueryDefinition(doc: DocumentNode): OperationDefinitionNode; // @public (undocumented) export const getStoreKeyName: ((fieldName: string, args?: Record | null, directives?: Directives) => string) & { - setStringify(s: typeof stringify): (value: any) => string; + setStringify(s: typeof storeKeyNameStringify): (value: any) => string; }; // @public (undocumented) @@ -1133,6 +1251,15 @@ interface IdGetterObj extends Object { _id?: string; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) export type InclusionDirectives = Array<{ directive: DirectiveNode; @@ -1181,6 +1308,10 @@ class InMemoryCache extends ApolloCache { resetResultCache?: boolean; resetResultIdentities?: boolean; }): string[]; + // Warning: (ae-forgotten-export) The symbol "getInMemoryCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getInMemoryCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // Warning: (ae-forgotten-export) The symbol "makeVar" needs to be exported by the entry point index.d.ts @@ -1217,7 +1348,7 @@ class InMemoryCache extends ApolloCache { // // @public (undocumented) interface InMemoryCacheConfig extends ApolloReducerConfig { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // Warning: (ae-forgotten-export) The symbol "FragmentRegistryAPI" needs to be exported by the entry point index.d.ts // @@ -1227,7 +1358,7 @@ interface InMemoryCacheConfig extends ApolloReducerConfig { // // (undocumented) possibleTypes?: PossibleTypesMap; - // (undocumented) + // @deprecated (undocumented) resultCacheMaxSize?: number; // (undocumented) resultCaching?: boolean; @@ -1319,8 +1450,6 @@ export function isQueryOperation(document: DocumentNode): boolean; // @public (undocumented) export function isReference(obj: any): obj is Reference; -// Warning: (ae-forgotten-export) The symbol "PromiseWithState" needs to be exported by the entry point index.d.ts -// // @public (undocumented) export function isStatefulPromise(promise: Promise): promise is PromiseWithState; @@ -1395,15 +1524,13 @@ class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -1543,31 +1670,20 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; // Warning: (ae-forgotten-export) The symbol "ErrorPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) errorPolicy?: ErrorPolicy; // Warning: (ae-forgotten-export) The symbol "OnQueryUpdated" needs to be exported by the entry point index.d.ts - // - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; // Warning: (ae-forgotten-export) The symbol "MutationUpdaterFunction" needs to be exported by the entry point index.d.ts - // - // (undocumented) update?: MutationUpdaterFunction; // Warning: (ae-forgotten-export) The symbol "MutationQueryReducersMap" needs to be exported by the entry point index.d.ts - // - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -1576,17 +1692,10 @@ interface MutationBaseOptions; -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts // // @public (undocumented) -interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1604,6 +1713,15 @@ type MutationQueryReducersMap; }; +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1622,21 +1740,14 @@ type MutationUpdaterFunction void; -// @public (undocumented) +// @public enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1660,7 +1771,7 @@ type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) +// @public interface NormalizedCache { // (undocumented) canRead: CanReadFunction; @@ -1686,17 +1797,14 @@ interface NormalizedCache { modify>(dataId: string, fields: Modifiers | AllFieldsModifier): boolean; // (undocumented) release(rootId: string): number; - // (undocumented) replace(newData: NormalizedCacheObject): void; - // (undocumented) retain(rootId: string): number; - // (undocumented) toObject(): NormalizedCacheObject; // (undocumented) toReference: ToReferenceFunction; } -// @public (undocumented) +// @public interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -1716,8 +1824,6 @@ class ObservableQuery; }); // Warning: (ae-forgotten-export) The symbol "FetchMoreQueryOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1742,12 +1848,13 @@ class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; // (undocumented) reobserveAsConcast(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1760,21 +1867,14 @@ class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; // Warning: (ae-forgotten-export) The symbol "SubscribeToMoreOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } @@ -1788,6 +1888,11 @@ export function offsetLimitPagination(keyArgs?: KeyArgs): FieldPo // @public (undocumented) export function omitDeep(value: T, key: K): DeepOmit; +// @public +export type OnlyRequiredProperties = { + [K in keyof T as {} extends Pick ? never : K]: T[K]; +}; + // @public (undocumented) type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1802,7 +1907,10 @@ interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1811,7 +1919,7 @@ interface Operation { type OperationVariables = Record; // @public (undocumented) -type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; +type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; // @public (undocumented) type Path = ReadonlyArray; @@ -1874,13 +1982,15 @@ type PossibleTypesMap = { type Primitive = null | undefined | string | number | boolean | symbol | bigint; // @public (undocumented) -const print_2: typeof print_3; +const print_2: ((ast: ASTNode) => string) & { + reset(): void; +}; export { print_2 as print } // Warning: (ae-forgotten-export) The symbol "PendingPromise" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type PromiseWithState = PendingPromise | FulfilledPromise | RejectedPromise; +export type PromiseWithState = PendingPromise | FulfilledPromise | RejectedPromise; // @public (undocumented) class QueryInfo { @@ -1896,7 +2006,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -1920,17 +2030,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -1944,7 +2056,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; @@ -1955,6 +2067,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -1964,6 +2077,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) @@ -1993,7 +2108,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -2007,7 +2124,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -2050,7 +2167,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -2062,27 +2178,19 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } @@ -2269,6 +2377,27 @@ type ServerParseError = Error & { bodyText: string; }; +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) export function shouldInclude({ directives }: SelectionNode, variables?: Record): boolean; @@ -2289,6 +2418,9 @@ type StorageType = Record; // @public (undocumented) export function storeKeyNameFromField(field: FieldNode, variables?: Object): string; +// @public (undocumented) +let storeKeyNameStringify: (value: any) => string; + // @public (undocumented) export interface StoreObject { // (undocumented) @@ -2298,14 +2430,11 @@ export interface StoreObject { } // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) export type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; -// @public (undocumented) -let stringify: (value: any) => string; - // @public (undocumented) export function stringifyForDisplay(value: any, space?: number): string; @@ -2316,7 +2445,7 @@ export function stripTypename(value: T): DeepOmit; class Stump extends Layer { constructor(root: EntityStore.Root); // (undocumented) - merge(): any; + merge(older: string | StoreObject, newer: string | StoreObject): void; // (undocumented) removeLayer(): this; } @@ -2332,15 +2461,10 @@ type SubscribeToMoreOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } @@ -2448,23 +2572,36 @@ export function valueToObjectRepresentation(argObj: any, name: NameNode, value: // @public (undocumented) export type VariableValue = (node: VariableNode) => any; +// @public +interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; + // @public (undocumented) type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; -// @public (undocumented) -interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// +// @public +interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // @public (undocumented) @@ -2502,27 +2639,25 @@ interface WriteContext extends ReadMergeModifyContext { // Warnings were encountered during analysis: // -// src/cache/core/types/DataProxy.ts:141:5 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:63:3 - (ae-forgotten-export) The symbol "TypePolicy" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:168:3 - (ae-forgotten-export) The symbol "FieldReadFunction" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:169:3 - (ae-forgotten-export) The symbol "FieldMergeFunction" needs to be exported by the entry point index.d.ts -// 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/cache/core/types/DataProxy.ts:146:7 - (ae-forgotten-export) The symbol "MissingFieldError" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:57:3 - (ae-forgotten-export) The symbol "TypePolicy" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:162:3 - (ae-forgotten-export) The symbol "FieldReadFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:163:3 - (ae-forgotten-export) The symbol "FieldMergeFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/types.ts:139:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts // src/cache/inmemory/writeToStore.ts:65:7 - (ae-forgotten-export) The symbol "MergeTree" needs to be exported by the entry point index.d.ts -// src/core/ApolloClient.ts:47:3 - (ae-forgotten-export) The symbol "UriFunction" needs to be exported by the entry point index.d.ts // 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/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 -// src/core/types.ts:205:5 - (ae-forgotten-export) The symbol "Resolver" 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/utilities/graphql/storeUtils.ts:220:12 - (ae-forgotten-export) The symbol "stringify" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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/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-utilities_globals.md b/.api-reports/api-report-utilities_globals.md index cf51100b464..b9fcd7f40d5 100644 --- a/.api-reports/api-report-utilities_globals.md +++ b/.api-reports/api-report-utilities_globals.md @@ -6,7 +6,7 @@ import { InvariantError } from 'ts-invariant'; -// @public (undocumented) +// @public @deprecated (undocumented) const DEV: boolean; export { DEV } export { DEV as __DEV__ } @@ -30,7 +30,7 @@ type LogFunction = { // @public (undocumented) export function maybe(thunk: () => T): T | undefined; -// @public (undocumented) +// @public export function newInvariantError(message?: string | number, ...optionalParams: unknown[]): InvariantError; // @public (undocumented) diff --git a/.api-reports/api-report-utilities_subscriptions_relay.md b/.api-reports/api-report-utilities_subscriptions_relay.md new file mode 100644 index 00000000000..4e77625a6e1 --- /dev/null +++ b/.api-reports/api-report-utilities_subscriptions_relay.md @@ -0,0 +1,28 @@ +## API Report File for "@apollo/client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { GraphQLResponse } from 'relay-runtime'; +import { Observable } from 'relay-runtime'; +import type { RequestParameters } from 'relay-runtime'; + +// Warning: (ae-forgotten-export) The symbol "CreateMultipartSubscriptionOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "OperationVariables" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function createFetchMultipartSubscription(uri: string, { fetch: preferredFetch, headers }?: CreateMultipartSubscriptionOptions): (operation: RequestParameters, variables: OperationVariables) => Observable; + +// @public (undocumented) +type CreateMultipartSubscriptionOptions = { + fetch?: WindowOrWorkerGlobalScope["fetch"]; + headers?: Record; +}; + +// @public (undocumented) +type OperationVariables = Record; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/.api-reports/api-report-utilities_subscriptions_urql.md b/.api-reports/api-report-utilities_subscriptions_urql.md new file mode 100644 index 00000000000..88c095736ad --- /dev/null +++ b/.api-reports/api-report-utilities_subscriptions_urql.md @@ -0,0 +1,25 @@ +## API Report File for "@apollo/client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { Observable } from 'zen-observable-ts'; + +// Warning: (ae-forgotten-export) The symbol "CreateMultipartSubscriptionOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export function createFetchMultipartSubscription(uri: string, { fetch: preferredFetch, headers }?: CreateMultipartSubscriptionOptions): ({ query, variables, }: { + query?: string; + variables: undefined | Record; +}) => Observable; + +// @public (undocumented) +type CreateMultipartSubscriptionOptions = { + fetch?: WindowOrWorkerGlobalScope["fetch"]; + headers?: Record; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/.api-reports/api-report.md b/.api-reports/api-report.md index 3004669da51..b3eb3f71625 100644 --- a/.api-reports/api-report.md +++ b/.api-reports/api-report.md @@ -4,8 +4,6 @@ ```ts -/// - import type { ASTNode } from 'graphql'; import { disableExperimentalFragmentVariables } from 'graphql-tag'; import { disableFragmentWarnings } from 'graphql-tag'; @@ -22,9 +20,7 @@ import { InvariantError } from 'ts-invariant'; import { Observable } from 'zen-observable-ts'; import type { Subscription as ObservableSubscription } from 'zen-observable-ts'; import type { Observer } from 'zen-observable-ts'; -import { print as print_3 } from 'graphql'; -import * as React_2 from 'react'; -import { ReactNode } from 'react'; +import type * as ReactTypes from 'react'; import { resetCaches } from 'graphql-tag'; import type { SelectionSetNode } from 'graphql'; import { setVerbosity as setLogVerbosity } from 'ts-invariant'; @@ -49,10 +45,13 @@ export abstract class ApolloCache implements DataProxy { abstract diff(query: Cache_2.DiffOptions): Cache_2.DiffResult; // (undocumented) abstract evict(options: Cache_2.EvictOptions): boolean; - // (undocumented) abstract extract(optimistic?: boolean): TSerialized; // (undocumented) gc(): string[]; + // Warning: (ae-forgotten-export) The symbol "getApolloCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getApolloCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -71,7 +70,6 @@ export abstract class ApolloCache implements DataProxy { abstract removeOptimistic(id: string): void; // (undocumented) abstract reset(options?: Cache_2.ResetOptions): Promise; - // (undocumented) abstract restore(serializedState: TSerialized): ApolloCache; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; @@ -83,6 +81,7 @@ export abstract class ApolloCache implements DataProxy { updateQuery(options: Cache_2.UpdateQueryOptions, update: (data: TData | null) => TData | null | void): TData | null; // (undocumented) abstract watch(watch: Cache_2.WatchOptions): () => void; + watchFragment(options: WatchFragmentOptions): Observable>; // (undocumented) abstract write(write: Cache_2.WriteOptions): Reference | undefined; // (undocumented) @@ -91,107 +90,95 @@ export abstract class ApolloCache implements DataProxy { writeQuery({ id, data, ...options }: Cache_2.WriteQueryOptions): Reference | undefined; } -// @public (undocumented) +// @public export class ApolloClient implements DataProxy { // (undocumented) __actionHookForDevTools(cb: () => any): void; constructor(options: ApolloClientOptions); // (undocumented) __requestRaw(payload: GraphQLRequest): Observable; - // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) cache: ApolloCache; - // (undocumented) clearStore(): Promise; // (undocumented) + get defaultContext(): Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) disableNetworkFetches: boolean; - // (undocumented) get documentTransform(): DocumentTransform; - // (undocumented) extract(optimistic?: boolean): TCacheShape; - // (undocumented) + // Warning: (ae-forgotten-export) The symbol "getApolloClientMemoryInternals" needs to be exported by the entry point index.d.ts + getMemoryInternals?: typeof getApolloClientMemoryInternals; getObservableQueries(include?: RefetchQueriesInclude): Map>; - // (undocumented) getResolvers(): Resolvers; // (undocumented) link: ApolloLink; - // (undocumented) mutate = DefaultContext, TCache extends ApolloCache = ApolloCache>(options: MutationOptions): Promise>; - // (undocumented) onClearStore(cb: () => Promise): () => void; - // (undocumented) onResetStore(cb: () => Promise): () => void; - // (undocumented) query(options: QueryOptions): Promise>; // (undocumented) queryDeduplication: boolean; - // (undocumented) readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; - // (undocumented) readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; - // (undocumented) reFetchObservableQueries(includeStandby?: boolean): Promise[]>; - // (undocumented) refetchQueries = ApolloCache, TResult = Promise>>(options: RefetchQueriesOptions): RefetchQueriesResult; - // (undocumented) resetStore(): Promise[] | null>; - // (undocumented) restore(serializedState: TCacheShape): ApolloCache; - // (undocumented) setLink(newLink: ApolloLink): void; - // (undocumented) setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; - // (undocumented) setResolvers(resolvers: Resolvers | Resolvers[]): void; - // (undocumented) stop(): void; - // (undocumented) subscribe(options: SubscriptionOptions): Observable>; // (undocumented) readonly typeDefs: ApolloClientOptions["typeDefs"]; // (undocumented) version: string; - // (undocumented) + watchFragment(options: WatchFragmentOptions): Observable>; watchQuery(options: WatchQueryOptions): ObservableQuery; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } // @public (undocumented) -export type ApolloClientOptions = { - uri?: string | UriFunction; +export interface ApolloClientOptions { + assumeImmutableResults?: boolean; + cache: ApolloCache; + connectToDevTools?: boolean; + // (undocumented) credentials?: string; + // (undocumented) + defaultContext?: Partial; + defaultOptions?: DefaultOptions; + // (undocumented) + documentTransform?: DocumentTransform; + // (undocumented) + fragmentMatcher?: FragmentMatcher; headers?: Record; link?: ApolloLink; - cache: ApolloCache; - ssrForceFetchDelay?: number; - ssrMode?: boolean; - connectToDevTools?: boolean; + name?: string; queryDeduplication?: boolean; - defaultOptions?: DefaultOptions; - assumeImmutableResults?: boolean; + // (undocumented) resolvers?: Resolvers | Resolvers[]; + ssrForceFetchDelay?: number; + ssrMode?: boolean; + // (undocumented) typeDefs?: string | string[] | DocumentNode | DocumentNode[]; - fragmentMatcher?: FragmentMatcher; - name?: string; + uri?: string | UriFunction; version?: string; - documentTransform?: DocumentTransform; -}; +} // Warning: (ae-forgotten-export) The symbol "ApolloConsumerProps" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const ApolloConsumer: React_2.FC; +export const ApolloConsumer: ReactTypes.FC; // @public (undocumented) interface ApolloConsumerProps { // (undocumented) - children: (client: ApolloClient) => React_2.ReactChild | null; + children: (client: ApolloClient) => ReactTypes.ReactNode; } // @public (undocumented) @@ -261,10 +248,16 @@ export class ApolloLink { static execute(link: ApolloLink, operation: GraphQLRequest): Observable; // (undocumented) static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // @internal + getMemoryInternals?: () => unknown; + // @internal + readonly left?: ApolloLink; // (undocumented) protected onError(error: any, observer?: Observer): false | void; // (undocumented) request(operation: Operation, forward?: NextLink): Observable | null; + // @internal + readonly right?: ApolloLink; // (undocumented) setOnError(fn: ApolloLink["onError"]): this; // (undocumented) @@ -284,25 +277,29 @@ export interface ApolloPayloadResult, TExtensions = // Warning: (ae-forgotten-export) The symbol "ApolloProviderProps" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export const ApolloProvider: React_2.FC>; +export const ApolloProvider: ReactTypes.FC>; // @public (undocumented) interface ApolloProviderProps { // (undocumented) - children: React_2.ReactNode | React_2.ReactNode[] | null; + children: ReactTypes.ReactNode | ReactTypes.ReactNode[] | null; // (undocumented) client: ApolloClient; } // @public (undocumented) -export type ApolloQueryResult = { +export interface ApolloQueryResult { + // (undocumented) data: T; - errors?: ReadonlyArray; error?: ApolloError; + errors?: ReadonlyArray; + // (undocumented) loading: boolean; + // (undocumented) networkStatus: NetworkStatus; + // (undocumented) partial?: boolean; -}; +} // @public (undocumented) export type ApolloReducerConfig = { @@ -310,7 +307,7 @@ export type ApolloReducerConfig = { addTypename?: boolean; }; -// @public (undocumented) +// @public type AsStoreObject = { @@ -326,60 +323,47 @@ export interface BackgroundQueryHookOptions = BackgroundQueryHookOptions, NoInfer>; +type BackgroundQueryHookOptionsNoInfer = BackgroundQueryHookOptions, NoInfer_2>; +// Warning: (ae-forgotten-export) The symbol "MutationSharedOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export interface BaseMutationOptions = ApolloCache> extends Omit, "mutation"> { - // (undocumented) +export interface BaseMutationOptions = ApolloCache> extends MutationSharedOptions { client?: ApolloClient; - // (undocumented) ignoreResults?: boolean; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; - // (undocumented) onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; } +// Warning: (ae-forgotten-export) The symbol "SharedWatchQueryOptions" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export interface BaseQueryOptions extends Omit, "query"> { - // (undocumented) +export interface BaseQueryOptions extends SharedWatchQueryOptions { client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) ssr?: boolean; } // @public (undocumented) export interface BaseSubscriptionOptions { - // (undocumented) client?: ApolloClient; - // (undocumented) context?: DefaultContext; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) onComplete?: () => void; - // (undocumented) onData?: (options: OnDataOptions) => any; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) + // @deprecated onSubscriptionComplete?: () => void; - // (undocumented) + // @deprecated onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; - // (undocumented) shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean); - // (undocumented) skip?: boolean; - // (undocumented) variables?: TVariables; } @@ -403,7 +387,7 @@ namespace Cache_2 { // (undocumented) interface BatchOptions, TUpdateResult = void> { // (undocumented) - onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => any; + onWatchUpdated?: (this: TCache, watch: Cache_2.WatchOptions, diff: Cache_2.DiffResult, lastDiff?: Cache_2.DiffResult | undefined) => any; // (undocumented) optimistic?: string | boolean; // (undocumented) @@ -441,7 +425,7 @@ namespace Cache_2 { } // (undocumented) interface ReadOptions extends DataProxy.Query { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // (undocumented) optimistic: boolean; @@ -503,13 +487,6 @@ class CacheGroup { resetCaching(): void; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -524,7 +501,7 @@ const enum CacheWriteBehavior { type CanReadFunction = (value: StoreValue) => boolean; // @public (undocumented) -export const checkFetcher: (fetcher: WindowOrWorkerGlobalScope["fetch"] | undefined) => void; +export const checkFetcher: (fetcher: typeof fetch | undefined) => void; // @public (undocumented) export type ClientParseError = InvariantError & { @@ -550,7 +527,7 @@ class Concast extends Observable { // (undocumented) cancel: (reason: any) => void; // (undocumented) - readonly promise: Promise; + readonly promise: Promise; // (undocumented) removeObserver(observer: Observer): void; } @@ -566,7 +543,10 @@ export const concat: typeof ApolloLink.concat; // @public (undocumented) export const createHttpLink: (linkOptions?: HttpOptions) => ApolloLink; -// @public (undocumented) +// @public +export function createQueryPreloader(client: ApolloClient): PreloadQueryFunction; + +// @public @deprecated (undocumented) export const createSignalIfSupported: () => { controller: boolean; signal: boolean; @@ -586,40 +566,29 @@ export namespace DataProxy { }; // (undocumented) export interface Fragment { - // (undocumented) fragment: DocumentNode | TypedDocumentNode; - // (undocumented) fragmentName?: string; - // (undocumented) id?: string; - // (undocumented) variables?: TVariables; } // (undocumented) export interface Query { - // (undocumented) id?: string; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // (undocumented) export interface ReadFragmentOptions extends Fragment { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) export interface ReadQueryOptions extends Query { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) optimistic?: boolean; - // (undocumented) returnPartialData?: boolean; } // (undocumented) @@ -633,11 +602,8 @@ export namespace DataProxy { } // (undocumented) export interface WriteOptions { - // (undocumented) broadcast?: boolean; - // (undocumented) data: TData; - // (undocumented) overwrite?: boolean; } // (undocumented) @@ -645,15 +611,11 @@ export namespace DataProxy { } } -// @public (undocumented) +// @public export interface DataProxy { - // (undocumented) readFragment(options: DataProxy.ReadFragmentOptions, optimistic?: boolean): FragmentType | null; - // (undocumented) readQuery(options: DataProxy.ReadQueryOptions, optimistic?: boolean): QueryType | null; - // (undocumented) writeFragment(options: DataProxy.WriteFragmentOptions): Reference | undefined; - // (undocumented) writeQuery(options: DataProxy.WriteQueryOptions): Reference | undefined; } @@ -665,7 +627,7 @@ export interface DataProxy { // Warning: (ae-forgotten-export) The symbol "DeepPartialObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends ReadonlyArray ? TItem[] extends T ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; +type DeepPartial = T extends DeepPartialPrimitive ? T : T extends Map ? DeepPartialMap : T extends ReadonlyMap ? DeepPartialReadonlyMap : T extends Set ? DeepPartialSet : T extends ReadonlySet ? DeepPartialReadonlySet : T extends (...args: any[]) => unknown ? T | undefined : T extends object ? T extends (ReadonlyArray) ? TItem[] extends (T) ? readonly TItem[] extends T ? ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject : unknown; // Warning: (ae-forgotten-export) The symbol "DeepPartial" needs to be exported by the entry point index.d.ts // @@ -745,14 +707,17 @@ export class DocumentTransform { // (undocumented) concat(otherTransform: DocumentTransform): DocumentTransform; // (undocumented) - getStableCacheEntry(document: DocumentNode): { - key: DocumentTransformCacheKey; - value?: DocumentNode | undefined; - } | undefined; - // (undocumented) static identity(): DocumentTransform; - // (undocumented) - static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform; + // @internal + readonly left?: DocumentTransform; + resetCache(): void; + // @internal + readonly right?: DocumentTransform; + // (undocumented) + static split(predicate: (document: DocumentNode) => boolean, left: DocumentTransform, right?: DocumentTransform): DocumentTransform & { + left: DocumentTransform; + right: DocumentTransform; + }; // (undocumented) transformDocument(document: DocumentNode): DocumentNode; } @@ -762,9 +727,7 @@ export type DocumentTransformCacheKey = ReadonlyArray; // @public (undocumented) interface DocumentTransformOptions { - // (undocumented) cache?: boolean; - // (undocumented) getCacheKey?: (document: DocumentNode) => DocumentTransformCacheKey | undefined; } @@ -829,7 +792,10 @@ abstract class EntityStore implements NormalizedCache { has(dataId: string): boolean; // (undocumented) protected lookup(dataId: string, dependOnExistence?: boolean): StoreObject | undefined; - // (undocumented) + makeCacheKey(document: DocumentNode, callback: Cache_2.WatchCallback, details: string): object; + makeCacheKey(selectionSet: SelectionSetNode, parent: string | StoreObject, varString: string | undefined, canonizeResults: boolean): object; + makeCacheKey(field: FieldNode, array: readonly any[], varString: string | undefined): object; + // @deprecated (undocumented) makeCacheKey(...args: any[]): object; // (undocumented) merge(older: string | StoreObject, newer: StoreObject | string): void; @@ -879,7 +845,7 @@ namespace EntityStore { } } -// @public (undocumented) +// @public export type ErrorPolicy = "none" | "ignore" | "all"; // @public (undocumented) @@ -949,20 +915,15 @@ export interface FetchMoreOptions }) => TData; } -// @public (undocumented) -type FetchMoreOptions_2 = Parameters["fetchMore"]>[0]; - // @public (undocumented) export interface FetchMoreQueryOptions { // (undocumented) context?: DefaultContext; - // (undocumented) query?: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: Partial; } -// @public (undocumented) +// @public export type FetchPolicy = "cache-first" | "network-only" | "cache-only" | "no-cache" | "standby"; // @public (undocumented) @@ -1037,7 +998,7 @@ type FieldValueGetter = EntityStore["getFieldValue"]; // @public (undocumented) type FlavorableWriteContext = Pick; -// @public (undocumented) +// @public interface FragmentMap { // (undocumented) [fragmentName: string]: FragmentDefinitionNode; @@ -1056,6 +1017,8 @@ interface FragmentRegistryAPI { // (undocumented) register(...fragments: DocumentNode[]): this; // (undocumented) + resetCaches(): void; + // (undocumented) transform(document: D): D; } @@ -1068,8 +1031,70 @@ export function fromError(errorValue: any): Observable; // @public (undocumented) export function fromPromise(promise: Promise): Observable; +// @internal +const getApolloCacheMemoryInternals: (() => { + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; + +// @internal +const getApolloClientMemoryInternals: (() => { + limits: { + [k: string]: number; + }; + sizes: { + cache?: { + fragmentQueryDocuments: number | undefined; + } | undefined; + addTypenameDocumentTransform?: { + cache: number; + }[] | undefined; + inMemoryCache?: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + } | undefined; + fragmentRegistry?: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + } | undefined; + print: number | undefined; + parser: number | undefined; + canonicalStringify: number | undefined; + links: unknown[]; + queryManager: { + getDocumentInfo: number; + documentTransforms: { + cache: number; + }[]; + }; + }; +}) | undefined; + // @public (undocumented) -export function getApolloContext(): React_2.Context; +export function getApolloContext(): ReactTypes.Context; + +// @internal +const getInMemoryCacheMemoryInternals: (() => { + addTypenameDocumentTransform: { + cache: number; + }[]; + inMemoryCache: { + executeSelectionSet: number | undefined; + executeSubSelectedArray: number | undefined; + maybeBroadcastWatch: number | undefined; + }; + fragmentRegistry: { + findFragmentSpreads: number | undefined; + lookup: number | undefined; + transform: number | undefined; + }; + cache: { + fragmentQueryDocuments: number | undefined; + }; +}) | undefined; export { gql } @@ -1107,31 +1132,19 @@ export class HttpLink extends ApolloLink { constructor(options?: HttpOptions); // (undocumented) options: HttpOptions; - // (undocumented) - requester: RequestHandler; } // @public (undocumented) export interface HttpOptions { - // (undocumented) credentials?: string; - // (undocumented) - fetch?: WindowOrWorkerGlobalScope["fetch"]; - // (undocumented) + fetch?: typeof fetch; fetchOptions?: any; - // (undocumented) headers?: Record; - // (undocumented) includeExtensions?: boolean; - // (undocumented) includeUnusedVariables?: boolean; - // (undocumented) preserveHeaderCase?: boolean; - // (undocumented) print?: Printer; - // (undocumented) uri?: string | UriFunction; - // (undocumented) useGETForQueries?: boolean; } @@ -1168,6 +1181,15 @@ export interface IDocumentDefinition { variables: ReadonlyArray; } +// @public (undocumented) +interface IgnoreModifier { + // (undocumented) + [_ignoreModifier]: true; +} + +// @public (undocumented) +const _ignoreModifier: unique symbol; + // @public (undocumented) export interface IncrementalPayload { // (undocumented) @@ -1206,6 +1228,10 @@ export class InMemoryCache extends ApolloCache { resetResultCache?: boolean; resetResultIdentities?: boolean; }): string[]; + // Warning: (ae-forgotten-export) The symbol "getInMemoryCacheMemoryInternals" needs to be exported by the entry point index.d.ts + // + // @internal + getMemoryInternals?: typeof getInMemoryCacheMemoryInternals; // (undocumented) identify(object: StoreObject | Reference): string | undefined; // (undocumented) @@ -1238,7 +1264,7 @@ export class InMemoryCache extends ApolloCache { // @public (undocumented) export interface InMemoryCacheConfig extends ApolloReducerConfig { - // (undocumented) + // @deprecated (undocumented) canonizeResults?: boolean; // Warning: (ae-forgotten-export) The symbol "FragmentRegistryAPI" needs to be exported by the entry point index.d.ts // @@ -1246,7 +1272,7 @@ export interface InMemoryCacheConfig extends ApolloReducerConfig { fragments?: FragmentRegistryAPI; // (undocumented) possibleTypes?: PossibleTypesMap; - // (undocumented) + // @deprecated (undocumented) resultCacheMaxSize?: number; // (undocumented) resultCaching?: boolean; @@ -1254,54 +1280,6 @@ export interface InMemoryCacheConfig extends ApolloReducerConfig { typePolicies?: TypePolicies; } -// @public (undocumented) -class InternalQueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReferenceOptions" needs to be exported by the entry point index.d.ts - constructor(observable: ObservableQuery, options: InternalQueryReferenceOptions); - // (undocumented) - applyOptions(watchQueryOptions: ObservedOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "ObservedOptions" needs to be exported by the entry point index.d.ts - // - // (undocumented) - didChangeOptions(watchQueryOptions: ObservedOptions): boolean; - // Warning: (ae-forgotten-export) The symbol "FetchMoreOptions_2" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchMore(options: FetchMoreOptions_2): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts - // - // (undocumented) - readonly key: CacheKey; - // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts - // - // (undocumented) - listen(listener: Listener): () => void; - // (undocumented) - readonly observable: ObservableQuery; - // (undocumented) - promise: Promise>; - // (undocumented) - promiseCache?: Map>>; - // (undocumented) - refetch(variables: OperationVariables | undefined): Promise>; - // (undocumented) - result: ApolloQueryResult; - // (undocumented) - retain(): () => void; - // (undocumented) - get watchQueryOptions(): WatchQueryOptions; -} - -// @public (undocumented) -interface InternalQueryReferenceOptions { - // (undocumented) - autoDisposeTimeoutMs?: number; - // (undocumented) - key: CacheKey; - // (undocumented) - onDispose?: () => void; -} - // Warning: (ae-forgotten-export) The symbol "RefetchQueriesIncludeShorthand" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -1336,7 +1314,7 @@ const _invalidateModifier: unique symbol; // @public (undocumented) export function isApolloError(err: Error): err is ApolloError; -// @public (undocumented) +// @public export function isNetworkRequestSettled(networkStatus?: NetworkStatus): boolean; // @public (undocumented) @@ -1405,32 +1383,55 @@ export interface LazyQueryHookExecOptions extends Omit, "skip"> { +export interface LazyQueryHookOptions extends BaseQueryOptions { + // @internal (undocumented) + defaultOptions?: Partial>; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; } -// @public (undocumented) +// @public @deprecated (undocumented) export type LazyQueryResult = QueryResult; // @public (undocumented) -export type LazyQueryResultTuple = [LazyQueryExecFunction, QueryResult]; +export type LazyQueryResultTuple = [ +execute: LazyQueryExecFunction, +result: QueryResult +]; + +// @public (undocumented) +export type LoadableQueryHookFetchPolicy = Extract; + +// @public (undocumented) +export interface LoadableQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: LoadableQueryHookFetchPolicy; + queryKey?: string | number | any[]; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; +} +// Warning: (ae-forgotten-export) The symbol "OnlyRequiredProperties" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -type Listener = (promise: Promise>) => void; +export type LoadQueryFunction = (...args: [TVariables] extends [never] ? [] : {} extends OnlyRequiredProperties ? [variables?: TVariables] : [variables: TVariables]) => void; // @public (undocumented) class LocalState { // Warning: (ae-forgotten-export) The symbol "LocalStateOptions" needs to be exported by the entry point index.d.ts constructor({ cache, client, resolvers, fragmentMatcher, }: LocalStateOptions); // (undocumented) - addExportedVariables(document: DocumentNode, variables?: OperationVariables, context?: {}): Promise<{ - [x: string]: any; - }>; + addExportedVariables(document: DocumentNode, variables?: TVars, context?: {}): Promise; // (undocumented) addResolvers(resolvers: Resolvers | Resolvers[]): void; // (undocumented) clientQuery(document: DocumentNode): DocumentNode | null; // (undocumented) - getFragmentMatcher(): FragmentMatcher; + getFragmentMatcher(): FragmentMatcher | undefined; // (undocumented) getResolvers(): Resolvers; // (undocumented) @@ -1553,23 +1554,16 @@ type Modifiers = Record> = Partia // @public (undocumented) interface MutationBaseOptions = ApolloCache> { - // (undocumented) awaitRefetchQueries?: boolean; - // (undocumented) context?: TContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) onQueryUpdated?: OnQueryUpdated; - // (undocumented) - optimisticResponse?: TData | ((vars: TVariables) => TData); - // (undocumented) + optimisticResponse?: TData | ((vars: TVariables, { IGNORE }: { + IGNORE: IgnoreModifier; + }) => TData); refetchQueries?: ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; - // (undocumented) update?: MutationUpdaterFunction; - // (undocumented) updateQueries?: MutationQueryReducersMap; - // (undocumented) variables?: TVariables; } @@ -1580,14 +1574,13 @@ export interface MutationDataOptions; +export type MutationFetchPolicy = Extract; // @public (undocumented) export type MutationFunction = ApolloCache> = (options?: MutationFunctionOptions) => Promise>; // @public (undocumented) export interface MutationFunctionOptions = ApolloCache> extends BaseMutationOptions { - // (undocumented) mutation?: DocumentNode | TypedDocumentNode; } @@ -1595,17 +1588,8 @@ export interface MutationFunctionOptions = ApolloCache> extends BaseMutationOptions { } -// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -export interface MutationOptions = ApolloCache> extends MutationBaseOptions { - // Warning: (ae-forgotten-export) The symbol "MutationFetchPolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fetchPolicy?: MutationFetchPolicy; - // (undocumented) - keepRootFields?: boolean; - // (undocumented) +export interface MutationOptions = ApolloCache> extends MutationSharedOptions { mutation: DocumentNode | TypedDocumentNode; } @@ -1625,20 +1609,22 @@ export type MutationQueryReducersMap { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data?: TData | null; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) reset(): void; } +// Warning: (ae-forgotten-export) The symbol "MutationBaseOptions" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +interface MutationSharedOptions = ApolloCache> extends MutationBaseOptions { + fetchPolicy?: MutationFetchPolicy; + keepRootFields?: boolean; +} + // @public (undocumented) interface MutationStoreValue { // (undocumented) @@ -1653,11 +1639,11 @@ interface MutationStoreValue { // @public (undocumented) export type MutationTuple = ApolloCache> = [ -(options?: MutationFunctionOptions) => Promise>, -MutationResult +mutate: (options?: MutationFunctionOptions) => Promise>, +result: MutationResult ]; -// @public (undocumented) +// @public @deprecated (undocumented) export type MutationUpdaterFn = (cache: ApolloCache, mutationResult: FetchResult) => void; @@ -1668,21 +1654,14 @@ export type MutationUpdaterFunction void; -// @public (undocumented) +// @public export enum NetworkStatus { - // (undocumented) error = 8, - // (undocumented) fetchMore = 3, - // (undocumented) loading = 1, - // (undocumented) poll = 6, - // (undocumented) ready = 7, - // (undocumented) refetch = 4, - // (undocumented) setVariables = 2 } @@ -1704,10 +1683,11 @@ export type NextLink = (operation: Operation) => Observable; // @public (undocumented) type NextResultListener = (method: "next" | "error" | "complete", arg?: any) => any; -// @public (undocumented) -export type NoInfer = [T][T extends any ? 0 : never]; +// @public +type NoInfer_2 = [T][T extends any ? 0 : never]; +export { NoInfer_2 as NoInfer } -// @public (undocumented) +// @public export interface NormalizedCache { // (undocumented) canRead: CanReadFunction; @@ -1733,17 +1713,14 @@ export interface NormalizedCache { modify>(dataId: string, fields: Modifiers | AllFieldsModifier): boolean; // (undocumented) release(rootId: string): number; - // (undocumented) replace(newData: NormalizedCacheObject): void; - // (undocumented) retain(rootId: string): number; - // (undocumented) toObject(): NormalizedCacheObject; // (undocumented) toReference: ToReferenceFunction; } -// @public (undocumented) +// @public export interface NormalizedCacheObject { // (undocumented) [dataId: string]: StoreObject | undefined; @@ -1762,7 +1739,6 @@ export class ObservableQuery; }); - // (undocumented) fetchMore(fetchMoreOptions: FetchMoreQueryOptions & { updateQuery?: (previousQueryResult: TData, options: { fetchMoreResult: TFetchData; @@ -1787,7 +1763,6 @@ export class ObservableQuery): Promise>; // (undocumented) reobserve(newOptions?: Partial>, newNetworkStatus?: NetworkStatus): Promise>; @@ -1795,6 +1770,8 @@ export class ObservableQuery>, newNetworkStatus?: NetworkStatus): Concast>; + // @internal (undocumented) + resetDiff(): void; // (undocumented) resetLastResults(): void; // (undocumented) @@ -1807,35 +1784,36 @@ export class ObservableQuery>; // (undocumented) setOptions(newOptions: Partial>): Promise>; - // (undocumented) setVariables(variables: TVariables): Promise | void>; // (undocumented) silentSetOptions(newOptions: Partial>): void; - // (undocumented) startPolling(pollInterval: number): void; - // (undocumented) stopPolling(): void; - // (undocumented) subscribeToMore(options: SubscribeToMoreOptions): () => void; - // (undocumented) updateQuery(mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData): void; - // (undocumented) get variables(): TVariables | undefined; } // @public (undocumented) -export type ObservableQueryFields = Pick, "startPolling" | "stopPolling" | "subscribeToMore" | "updateQuery" | "refetch" | "reobserve" | "variables" | "fetchMore">; +export interface ObservableQueryFields { + fetchMore: (fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: (previousQueryResult: TData, options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + }) => TData; + }) => Promise>; + refetch: (variables?: Partial) => Promise>; + // @internal (undocumented) + reobserve: (newOptions?: Partial>, newNetworkStatus?: NetworkStatus) => Promise>; + startPolling: (pollInterval: number) => void; + stopPolling: () => void; + subscribeToMore: (options: SubscribeToMoreOptions) => () => void; + updateQuery: (mapFn: (previousQueryResult: TData, options: Pick, "variables">) => TData) => void; + variables: TVariables | undefined; +} export { ObservableSubscription } -// @public (undocumented) -const OBSERVED_CHANGED_OPTIONS: readonly ["canonizeResults", "context", "errorPolicy", "fetchPolicy", "refetchWritePolicy", "returnPartialData"]; - -// Warning: (ae-forgotten-export) The symbol "OBSERVED_CHANGED_OPTIONS" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -type ObservedOptions = Pick; - export { Observer } // @public (undocumented) @@ -1846,6 +1824,11 @@ export interface OnDataOptions { data: SubscriptionResult; } +// @public +type OnlyRequiredProperties = { + [K in keyof T as {} extends Pick ? never : K]: T[K]; +}; + // @public (undocumented) export type OnQueryUpdated = (observableQuery: ObservableQuery, diff: Cache_2.DiffResult, lastDiff: Cache_2.DiffResult | undefined) => boolean | TResult; @@ -1868,7 +1851,10 @@ export interface Operation { // (undocumented) query: DocumentNode; // (undocumented) - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + (updateContext: (previousContext: DefaultContext) => Partial): void; + }; // (undocumented) variables: Record; } @@ -1887,7 +1873,7 @@ export type OptimisticStoreItem = { }; // @public (undocumented) -type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; +type OptionsUnion = WatchQueryOptions | QueryOptions | MutationOptions; // @public (undocumented) export function parseAndCheckHttpResponse(operations: Operation | Operation[]): (response: Response) => Promise; @@ -1895,6 +1881,12 @@ export function parseAndCheckHttpResponse(operations: Operation | Operation[]): // @public (undocumented) export function parser(document: DocumentNode): IDocumentDefinition; +// @public (undocumented) +export namespace parser { + var // (undocumented) + resetCache: () => void; +} + // @public (undocumented) export type Path = ReadonlyArray; @@ -1945,11 +1937,59 @@ export type PossibleTypesMap = { [supertype: string]: string[]; }; +// @public +export interface PreloadedQueryRef extends QueryRef { + toPromise(): Promise>; +} + +// @public (undocumented) +export type PreloadQueryFetchPolicy = Extract; + +// @public +export interface PreloadQueryFunction { + // Warning: (ae-forgotten-export) The symbol "PreloadQueryOptionsArg" needs to be exported by the entry point index.d.ts + >(query: DocumentNode | TypedDocumentNode, ...[options]: PreloadQueryOptionsArg, TOptions>): PreloadedQueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables>; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; + }): PreloadedQueryRef | undefined, TVariables>; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + errorPolicy: "ignore" | "all"; + }): PreloadedQueryRef; + (query: DocumentNode | TypedDocumentNode, options: PreloadQueryOptions> & { + returnPartialData: true; + }): PreloadedQueryRef, TVariables>; + (query: DocumentNode | TypedDocumentNode, ...[options]: PreloadQueryOptionsArg>): PreloadedQueryRef; +} + +// Warning: (ae-forgotten-export) The symbol "VariablesOption" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type PreloadQueryOptions = { + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: PreloadQueryFetchPolicy; + returnPartialData?: boolean; + refetchWritePolicy?: RefetchWritePolicy; +} & VariablesOption; + +// @public (undocumented) +type PreloadQueryOptionsArg = [TVariables] extends [never] ? [ +options?: PreloadQueryOptions & TOptions +] : {} extends OnlyRequiredProperties ? [ +options?: PreloadQueryOptions> & Omit +] : [ +options: PreloadQueryOptions> & Omit +]; + // @public (undocumented) type Primitive = null | undefined | string | number | boolean | symbol | bigint; // @public (undocumented) -const print_2: typeof print_3; +const print_2: ((ast: ASTNode) => string) & { + reset(): void; +}; // @public (undocumented) interface Printer { @@ -1960,7 +2000,7 @@ interface Printer { } // @public (undocumented) -const QUERY_REFERENCE_SYMBOL: unique symbol; +const QUERY_REF_BRAND: unique symbol; // @public (undocumented) interface QueryData { @@ -1973,20 +2013,16 @@ interface QueryData { // @public (undocumented) export interface QueryDataOptions extends QueryFunctionOptions { // (undocumented) - children?: (result: QueryResult) => ReactNode; - // (undocumented) + children?: (result: QueryResult) => ReactTypes.ReactNode; query: DocumentNode | TypedDocumentNode; } // @public (undocumented) -export interface QueryFunctionOptions extends BaseQueryOptions { - // (undocumented) +export interface QueryFunctionOptions extends BaseQueryOptions { + // @internal (undocumented) defaultOptions?: Partial>; - // (undocumented) onCompleted?: (data: TData) => void; - // (undocumented) onError?: (error: ApolloError) => void; - // (undocumented) skip?: boolean; } @@ -2008,7 +2044,7 @@ class QueryInfo { document: DocumentNode; variables: Record | undefined; networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this; // (undocumented) @@ -2030,17 +2066,19 @@ class QueryInfo { // (undocumented) notify(): void; // (undocumented) - readonly observableQuery: ObservableQuery | null; + readonly observableQuery: ObservableQuery | null; // (undocumented) readonly queryId: string; // (undocumented) reset(): void; // (undocumented) + resetDiff(): void; + // (undocumented) resetLastWrite(): void; // (undocumented) setDiff(diff: Cache_2.DiffResult | null): void; // (undocumented) - setObservableQuery(oq: ObservableQuery | null): void; + setObservableQuery(oq: ObservableQuery | null): void; // (undocumented) stop(): void; // (undocumented) @@ -2049,11 +2087,9 @@ class QueryInfo { variables?: Record; } -// @public (undocumented) +// @public @deprecated (undocumented) export interface QueryLazyOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) variables?: TVariables; } @@ -2062,7 +2098,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 +2109,7 @@ class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }); // (undocumented) readonly assumeImmutableResults: boolean; @@ -2083,6 +2120,8 @@ class QueryManager { // (undocumented) clearStore(options?: Cache_2.ResetOptions): Promise; // (undocumented) + readonly defaultContext: Partial; + // (undocumented) defaultOptions: DefaultOptions; // (undocumented) readonly documentTransform: DocumentTransform; @@ -2109,7 +2148,9 @@ class QueryManager { // (undocumented) getQueryStore(): Record; // (undocumented) - protected inFlightLinkObservables: Map>>; + protected inFlightLinkObservables: Trie<{ + observable?: Observable> | undefined; + }>; // (undocumented) link: ApolloLink; // (undocumented) @@ -2123,7 +2164,7 @@ class QueryManager { updateQueries: UpdateQueries; update?: MutationUpdaterFunction; keepRootFields?: boolean; - }): void; + }): boolean; // (undocumented) markMutationResult>(mutation: { mutationId: string; @@ -2163,7 +2204,6 @@ class QueryManager { readonly ssrMode: boolean; // (undocumented) startGraphQLSubscription({ query, fetchPolicy, errorPolicy, variables, context, }: SubscriptionOptions): Observable>; - // (undocumented) stop(): void; // (undocumented) stopQuery(queryId: string): void; @@ -2175,64 +2215,52 @@ class QueryManager { watchQuery(options: WatchQueryOptions): ObservableQuery; } -// @public (undocumented) +// @public interface QueryOptions { - // (undocumented) + // @deprecated canonizeResults?: boolean; - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) notifyOnNetworkStatusChange?: boolean; - // (undocumented) + // @deprecated partialRefetch?: boolean; - // (undocumented) pollInterval?: number; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) returnPartialData?: boolean; - // (undocumented) variables?: TVariables; } export { QueryOptions as PureQueryOptions } export { QueryOptions } -// @public (undocumented) -export interface QueryReference { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; +// @public +export interface QueryRef { + // @internal (undocumented) + [QUERY_REF_BRAND]?(variables: TVariables): TData; +} + +// @public @deprecated (undocumented) +export interface QueryReference extends QueryRef { + // @deprecated (undocumented) + toPromise?: unknown; } // @public (undocumented) export interface QueryResult extends ObservableQueryFields { - // (undocumented) called: boolean; - // (undocumented) client: ApolloClient; - // (undocumented) data: TData | undefined; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) networkStatus: NetworkStatus; - // (undocumented) observable: ObservableQuery; - // (undocumented) previousData?: TData; } // @public (undocumented) type QueryStoreValue = Pick; -// @public (undocumented) +// @public @deprecated (undocumented) export type QueryTuple = LazyQueryResultTuple; // @public (undocumented) @@ -2334,16 +2362,16 @@ export interface RefetchQueriesResult extends Promise(obsQuery: ObservableQuery): ReactNode; + addObservableQueryPromise(obsQuery: ObservableQuery): ReactTypes.ReactNode; // Warning: (ae-forgotten-export) The symbol "QueryData" needs to be exported by the entry point index.d.ts // // (undocumented) - addQueryPromise(queryInstance: QueryData, finish?: () => React.ReactNode): React.ReactNode; + addQueryPromise(queryInstance: QueryData, finish?: () => ReactTypes.ReactNode): ReactTypes.ReactNode; // (undocumented) consumeAndAwaitPromises(): Promise; // (undocumented) @@ -2359,11 +2387,14 @@ class RenderPromises { // @public (undocumented) export type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; -// @public (undocumented) +// @public @deprecated (undocumented) export const resetApolloContext: typeof getApolloContext; export { resetCaches } +// @public (undocumented) +type ResetFunction = () => void; + // @public (undocumented) export type Resolver = (rootValue?: any, args?: any, context?: any, info?: { field: FieldNode; @@ -2382,7 +2413,7 @@ export interface Resolvers { // // @public (undocumented) export function rewriteURIForGET(chosenURI: string, body: Body_2): { - parseError: any; + parseError: unknown; newURI?: undefined; } | { newURI: string; @@ -2407,7 +2438,7 @@ export function selectHttpOptionsAndBodyInternal(operation: Operation, printer: }; // @public (undocumented) -export const selectURI: (operation: Operation, fallbackURI?: string | ((operation: Operation) => string) | undefined) => any; +export const selectURI: (operation: Operation, fallbackURI?: string | ((operation: Operation) => string)) => any; // @public (undocumented) export const serializeFetchParameter: (p: any, label: string) => string; @@ -2428,6 +2459,26 @@ export type ServerParseError = Error & { export { setLogVerbosity } +// @public (undocumented) +interface SharedWatchQueryOptions { + // @deprecated + canonizeResults?: boolean; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; + fetchPolicy?: WatchQueryFetchPolicy; + initialFetchPolicy?: WatchQueryFetchPolicy; + // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts + nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); + notifyOnNetworkStatusChange?: boolean; + // @deprecated + partialRefetch?: boolean; + pollInterval?: number; + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + skipPollAttempt?: () => boolean; + variables?: TVariables; +} + // @public (undocumented) export interface SingleExecutionResult, TContext = DefaultContext, TExtensions = Record> extends ExecutionResult { // (undocumented) @@ -2462,7 +2513,7 @@ export interface StoreObject { // Warning: (ae-forgotten-export) The symbol "AsStoreObject" needs to be exported by the entry point index.d.ts // // @public (undocumented) -type StoreObjectValueMaybeReference = StoreVal extends Array> ? ReadonlyArray | Reference> : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; +type StoreObjectValueMaybeReference = StoreVal extends Array> ? StoreVal extends Array ? Item extends Record ? ReadonlyArray | Reference> : never : never : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; // @public (undocumented) export type StoreValue = number | string | string[] | Reference | Reference[] | null | undefined | void | Object; @@ -2471,7 +2522,7 @@ export type StoreValue = number | string | string[] | Reference | Reference[] | class Stump extends Layer { constructor(root: EntityStore.Root); // (undocumented) - merge(): any; + merge(older: string | StoreObject, newer: string | StoreObject): void; // (undocumented) removeLayer(): this; } @@ -2499,7 +2550,7 @@ export interface SubscriptionCurrentObservable { // @public (undocumented) export interface SubscriptionDataOptions extends BaseSubscriptionOptions { // (undocumented) - children?: null | ((result: SubscriptionResult) => JSX.Element | null); + children?: null | ((result: SubscriptionResult) => ReactTypes.ReactNode); // (undocumented) subscription: DocumentNode | TypedDocumentNode; } @@ -2510,27 +2561,19 @@ export interface SubscriptionHookOptions { - // (undocumented) context?: DefaultContext; - // (undocumented) errorPolicy?: ErrorPolicy; - // (undocumented) fetchPolicy?: FetchPolicy; - // (undocumented) query: DocumentNode | TypedDocumentNode; - // (undocumented) variables?: TVariables; } // @public (undocumented) export interface SubscriptionResult { - // (undocumented) data?: TData; - // (undocumented) error?: ApolloError; - // (undocumented) loading: boolean; - // (undocumented) + // @internal (undocumented) variables?: TVariables; } @@ -2538,13 +2581,19 @@ export interface SubscriptionResult { export type SuspenseQueryHookFetchPolicy = Extract; // @public (undocumented) -export interface SuspenseQueryHookOptions extends Pick, "client" | "variables" | "errorPolicy" | "context" | "canonizeResults" | "returnPartialData" | "refetchWritePolicy"> { - // (undocumented) +export interface SuspenseQueryHookOptions { + // @deprecated + canonizeResults?: boolean; + client?: ApolloClient; + context?: DefaultContext; + errorPolicy?: ErrorPolicy; fetchPolicy?: SuspenseQueryHookFetchPolicy; - // (undocumented) queryKey?: string | number | any[]; - // (undocumented) + refetchWritePolicy?: RefetchWritePolicy; + returnPartialData?: boolean; + // @deprecated skip?: boolean; + variables?: TVariables; } // @public (undocumented) @@ -2635,7 +2684,7 @@ export function useApolloClient(override?: ApolloClient): ApolloClient, "variables">>(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer & TOptions): [ -(QueryReference | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData> | (TOptions["skip"] extends boolean ? undefined : never)), +(QueryRef | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables> | (TOptions["skip"] extends boolean ? undefined : never)), UseBackgroundQueryResult ]; @@ -2644,7 +2693,7 @@ export function useBackgroundQuery | undefined>, +QueryRef | undefined, TVariables>, UseBackgroundQueryResult ]; @@ -2652,7 +2701,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { errorPolicy: "ignore" | "all"; }): [ -QueryReference, +QueryRef, UseBackgroundQueryResult ]; @@ -2661,7 +2710,7 @@ export function useBackgroundQuery> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; @@ -2669,7 +2718,7 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; }): [ -QueryReference>, +QueryRef, TVariables>, UseBackgroundQueryResult ]; @@ -2677,12 +2726,12 @@ UseBackgroundQueryResult export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: BackgroundQueryHookOptionsNoInfer & { skip: boolean; }): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; // @public (undocumented) -export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryReference, UseBackgroundQueryResult]; +export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer): [QueryRef, UseBackgroundQueryResult]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken): [undefined, UseBackgroundQueryResult]; @@ -2691,13 +2740,13 @@ export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (BackgroundQueryHookOptionsNoInfer & { returnPartialData: true; })): [ -QueryReference> | undefined, +QueryRef, TVariables> | undefined, UseBackgroundQueryResult ]; // @public (undocumented) export function useBackgroundQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | BackgroundQueryHookOptionsNoInfer): [ -QueryReference | undefined, +QueryRef | undefined, UseBackgroundQueryResult ]; @@ -2711,7 +2760,8 @@ export type UseBackgroundQueryResult(options: UseFragmentOptions): UseFragmentResult; // @public (undocumented) -export interface UseFragmentOptions extends Omit, NoInfer>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { +export interface UseFragmentOptions extends Omit, NoInfer_2>, "id" | "query" | "optimistic" | "previousResult" | "returnPartialData">, Omit, "id" | "variables" | "returnPartialData"> { + client?: ApolloClient; // (undocumented) from: StoreObject | Reference | string; // (undocumented) @@ -2729,74 +2779,113 @@ export type UseFragmentResult = { missing?: MissingTree; }; +// @public +export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer_2>): LazyQueryResultTuple; + +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions & TOptions): UseLoadableQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? DeepPartial : TData, TVariables>; + // @public (undocumented) -export function useLazyQuery(query: DocumentNode | TypedDocumentNode, options?: LazyQueryHookOptions, NoInfer>): LazyQueryResultTuple; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult | undefined, TVariables>; // @public (undocumented) -export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer, TContext, TCache>): MutationTuple; +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + errorPolicy: "ignore" | "all"; +}): UseLoadableQueryResult; + +// @public (undocumented) +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options: LoadableQueryHookOptions & { + returnPartialData: true; +}): UseLoadableQueryResult, TVariables>; + +// @public +export function useLoadableQuery(query: DocumentNode | TypedDocumentNode, options?: LoadableQueryHookOptions): UseLoadableQueryResult; // @public (undocumented) -export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer>): QueryResult; +export type UseLoadableQueryResult = [ +loadQuery: LoadQueryFunction, +queryRef: QueryRef | null, +handlers: { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; + reset: ResetFunction; +} +]; + +// @public +export function useMutation = ApolloCache>(mutation: DocumentNode | TypedDocumentNode, options?: MutationHookOptions, NoInfer_2, TContext, TCache>): MutationTuple; + +// @public +export function useQuery(query: DocumentNode | TypedDocumentNode, options?: QueryHookOptions, NoInfer_2>): QueryResult; + +// @public +export function useQueryRefHandlers(queryRef: QueryRef): UseQueryRefHandlersResult; // @public (undocumented) +export interface UseQueryRefHandlersResult { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; +} + +// @public export function useReactiveVar(rv: ReactiveVar): T; // @public (undocumented) -export function useReadQuery(queryRef: QueryReference): UseReadQueryResult; +export function useReadQuery(queryRef: QueryRef): UseReadQueryResult; // @public (undocumented) export interface UseReadQueryResult { - // (undocumented) data: TData; - // (undocumented) error: ApolloError | undefined; - // (undocumented) networkStatus: NetworkStatus; } -// @public (undocumented) -export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer>): SubscriptionResult; +// @public +export function useSubscription(subscription: DocumentNode | TypedDocumentNode, options?: SubscriptionHookOptions, NoInfer_2>): SubscriptionResult; // @public (undocumented) -export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; +export function useSuspenseQuery, "variables">>(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2> & TOptions): UseSuspenseQueryResult | undefined : TData | undefined : TOptions["returnPartialData"] extends true ? TOptions["skip"] extends boolean ? DeepPartial | undefined : DeepPartial : TOptions["skip"] extends boolean ? TData | undefined : TData, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { errorPolicy: "ignore" | "all"; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; returnPartialData: true; }): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; }): UseSuspenseQueryResult, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SuspenseQueryHookOptions, NoInfer_2> & { skip: boolean; }): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer> & { +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options: SkipToken | (SuspenseQueryHookOptions, NoInfer_2> & { returnPartialData: true; })): UseSuspenseQueryResult | undefined, TVariables>; // @public (undocumented) -export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer>): UseSuspenseQueryResult; +export function useSuspenseQuery(query: DocumentNode | TypedDocumentNode, options?: SkipToken | SuspenseQueryHookOptions, NoInfer_2>): UseSuspenseQueryResult; // @public (undocumented) export interface UseSuspenseQueryResult { @@ -2819,22 +2908,44 @@ export interface UseSuspenseQueryResult = [ +TVariables +] extends [never] ? { + variables?: Record; +} : {} extends OnlyRequiredProperties ? { + variables?: TVariables; +} : { + variables: TVariables; +}; + +// @public +export interface WatchFragmentOptions { + // @deprecated (undocumented) + canonizeResults?: boolean; + fragment: DocumentNode | TypedDocumentNode; + fragmentName?: string; + from: StoreObject | Reference | string; + optimistic?: boolean; + variables?: TVars; +} + +// @public +export type WatchFragmentResult = { + data: TData; + complete: true; + missing?: never; +} | { + data: DeepPartial; + complete: false; + missing: MissingTree; +}; // @public (undocumented) -export interface WatchQueryOptions extends Omit, "fetchPolicy"> { - // (undocumented) - fetchPolicy?: WatchQueryFetchPolicy; - // (undocumented) - initialFetchPolicy?: WatchQueryFetchPolicy; - // Warning: (ae-forgotten-export) The symbol "NextFetchPolicyContext" needs to be exported by the entry point index.d.ts - // - // (undocumented) - nextFetchPolicy?: WatchQueryFetchPolicy | ((this: WatchQueryOptions, currentFetchPolicy: WatchQueryFetchPolicy, context: NextFetchPolicyContext) => WatchQueryFetchPolicy); - // Warning: (ae-forgotten-export) The symbol "RefetchWritePolicy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - refetchWritePolicy?: RefetchWritePolicy; +export type WatchQueryFetchPolicy = FetchPolicy | "cache-and-network"; + +// @public +export interface WatchQueryOptions extends SharedWatchQueryOptions { + query: DocumentNode | TypedDocumentNode; } // @public (undocumented) @@ -2871,19 +2982,21 @@ interface WriteContext extends ReadMergeModifyContext { // Warnings were encountered during analysis: // -// src/cache/inmemory/policies.ts:98:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts -// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts -// 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/watchQueryOptions.ts:191:3 - (ae-forgotten-export) The symbol "UpdateQueryFn" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:92:3 - (ae-forgotten-export) The symbol "FragmentMap" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/policies.ts:161:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts +// src/cache/inmemory/types.ts:139:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts +// src/core/ObservableQuery.ts:116:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts +// 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/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 -// src/react/hooks/useBackgroundQuery.ts:25:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useBackgroundQuery.ts:29:3 - (ae-forgotten-export) The symbol "FetchMoreFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useBackgroundQuery.ts:30:3 - (ae-forgotten-export) The symbol "RefetchFunction" needs to be exported by the entry point index.d.ts +// src/react/hooks/useLoadableQuery.ts:107:1 - (ae-forgotten-export) The symbol "ResetFunction" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.changeset/brown-bikes-divide.md b/.changeset/brown-bikes-divide.md new file mode 100644 index 00000000000..8701c21eae9 --- /dev/null +++ b/.changeset/brown-bikes-divide.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +switch `useRenderGuard` to an approach not accessing React's internals diff --git a/.changeset/famous-camels-rescue.md b/.changeset/famous-camels-rescue.md new file mode 100644 index 00000000000..fbf8b08722b --- /dev/null +++ b/.changeset/famous-camels-rescue.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +`useLoadableQuery`: ensure that `loadQuery` is updated if the ApolloClient instance changes diff --git a/.changeset/good-experts-repair.md b/.changeset/good-experts-repair.md deleted file mode 100644 index 37aef92f934..00000000000 --- a/.changeset/good-experts-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@apollo/client": patch ---- - -Add an explicit return type for the `useReadQuery` hook called `UseReadQueryResult`. Previously the return type of this hook was inferred from the return value. diff --git a/.changeset/late-days-give.md b/.changeset/late-days-give.md new file mode 100644 index 00000000000..4fe36c2d495 --- /dev/null +++ b/.changeset/late-days-give.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Fixes [#11849](https://github.com/apollographql/apollo-client/issues/11849) by reevaluating `window.fetch` each time `BatchHttpLink` uses it, if not configured via `options.fetch`. Takes the same approach as PR [#8603](https://github.com/apollographql/apollo-client/pull/8603) which fixed the same issue in `HttpLink`. diff --git a/.changeset/loud-hairs-think.md b/.changeset/loud-hairs-think.md new file mode 100644 index 00000000000..7e29ffae5ec --- /dev/null +++ b/.changeset/loud-hairs-think.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Fix a bug where calling the `useMutation` `reset` function would point the hook to an outdated `client` reference. diff --git a/.changeset/mighty-monkeys-explain.md b/.changeset/mighty-monkeys-explain.md new file mode 100644 index 00000000000..4629cd11273 --- /dev/null +++ b/.changeset/mighty-monkeys-explain.md @@ -0,0 +1,6 @@ +--- +"@apollo/client": patch +--- + +Prevent writing to a ref in render in `useMutation`. +As a result, you might encounter problems in the future if you call the mutation's `execute` function during render. Please note that this was never supported behavior, and we strongly recommend against it. diff --git a/.changeset/nasty-pens-dress.md b/.changeset/nasty-pens-dress.md new file mode 100644 index 00000000000..7eb2f16b3c6 --- /dev/null +++ b/.changeset/nasty-pens-dress.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Ensure covariant behavior: `MockedResponse` should be assignable to `MockedResponse` diff --git a/.changeset/shaggy-mirrors-judge.md b/.changeset/shaggy-mirrors-judge.md new file mode 100644 index 00000000000..f5e599284ab --- /dev/null +++ b/.changeset/shaggy-mirrors-judge.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Avoid usage of useRef in useInternalState to prevent ref access in render. diff --git a/.changeset/sharp-cats-taste.md b/.changeset/sharp-cats-taste.md new file mode 100644 index 00000000000..6d2946fe28d --- /dev/null +++ b/.changeset/sharp-cats-taste.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Add missing name to tuple member (fix TS5084) diff --git a/.changeset/stupid-planes-nail.md b/.changeset/stupid-planes-nail.md new file mode 100644 index 00000000000..200a00ac26b --- /dev/null +++ b/.changeset/stupid-planes-nail.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Fix a bug where `useLazyQuery` would not pick up a client change. diff --git a/.circleci/config.yml b/.circleci/config.yml index aa78388ce83..f1aa7d88d82 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - secops: apollo/circleci-secops-orb@2.0.1 + secops: apollo/circleci-secops-orb@2.0.7 jobs: # Filesize: @@ -15,7 +15,7 @@ jobs: Lint: docker: - - image: cimg/node:21.1.0 + - image: cimg/node:22.2.0 steps: - checkout - run: npm version @@ -24,7 +24,7 @@ jobs: Formatting: docker: - - image: cimg/node:21.1.0 + - image: cimg/node:22.2.0 steps: - checkout - run: npm ci @@ -32,17 +32,21 @@ jobs: Tests: docker: - - image: cimg/node:21.1.0 + - image: cimg/node:22.2.0 + parameters: + project: + type: string steps: - checkout - run: npm run ci:precheck - run: npm version - run: npm ci + - run: if test "<< parameters.project >>" = "Core Tests"; then npm run test:memory; fi - run: name: Jest suite with coverage - command: npm run test:ci + command: npm run test:ci -- --selectProjects "<< parameters.project >>" environment: - JEST_JUNIT_OUTPUT_FILE: "reports/junit/js-test-results.xml" + JEST_JUNIT_OUTPUT_FILE: "reports/junit/js-test-results-<< parameters.project >>.xml" - store_test_results: path: reports/junit - store_artifacts: @@ -50,7 +54,7 @@ jobs: BuildTarball: docker: - - image: cimg/node:21.1.0 + - image: cimg/node:22.2.0 steps: - checkout - run: npm run ci:precheck @@ -66,22 +70,35 @@ jobs: parameters: framework: type: string + react: + type: string docker: - - image: cimg/node:21.1.0 + - image: cimg/node:22.2.0-browsers steps: - checkout - attach_workspace: at: /tmp/workspace - run: npm version + - run: + command: npm pkg set "overrides[@apollo/client]=/tmp/workspace/apollo-client.tgz" + working_directory: integration-tests + - run: + command: | + export VERSION=$(npm show react --json | jq '."dist-tags"."<< parameters.react >>"' -r) + npm pkg set "overrides[react]=${VERSION}" "overrides[react-dom]=${VERSION}" + working_directory: integration-tests - run: command: npm run ci-preparations --workspace=<< parameters.framework >> --if-present working_directory: integration-tests - run: - command: npm install @apollo/client@/tmp/workspace/apollo-client.tgz --workspace=<< parameters.framework >> + command: npm install working_directory: integration-tests - run: command: npx playwright install-deps working_directory: integration-tests + - run: + command: npx playwright install + working_directory: integration-tests - run: command: npm run build --workspace=<< parameters.framework >> --if-present working_directory: integration-tests @@ -89,16 +106,38 @@ jobs: command: npm run test --workspace=<< parameters.framework >> working_directory: integration-tests + TestPeerDepTypes: + parameters: + externalPackage: + type: string + docker: + - image: cimg/node:22.2.0 + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + working_directory: integration-tests/peerdeps-tsc + command: | + npm install + npm install @apollo/client@/tmp/workspace/apollo-client.tgz + npm install << parameters.externalPackage >> + npm test + workflows: Build and Test: jobs: # - Filesize - - Tests + - Tests: + matrix: + parameters: + project: + ["Core Tests", "ReactDOM 17", "ReactDOM 18", "ReactDOM 19"] - Formatting - Lint - BuildTarball - IntegrationTests: - name: Integration Test << matrix.framework >> + name: Integration Test << matrix.framework >> with React << matrix.react >> requires: - BuildTarball matrix: @@ -112,6 +151,30 @@ workflows: - vite - vite-swc # -browser-esm would need a package publish to npm/CDNs + react: + - latest + - next + exclude: + - framework: cra4 + react: next + # next ships it's own React version anyways, no need to run this test twice + - framework: next + react: next + - TestPeerDepTypes: + name: Test external types for << matrix.externalPackage >> + requires: + - BuildTarball + matrix: + parameters: + externalPackage: + - "graphql@15" + - "graphql@16" + - "graphql@^17.0.0-alpha" + - "@types/react@16.8 @types/react-dom@16.8" + - "@types/react@17 @types/react-dom@17" + - "@types/react@18 @types/react-dom@18" + - "@types/react@npm:types-react@19.0.0-rc.0 @types/react-dom@npm:types-react-dom@19.0.0-rc.0" + - "typescript@next" security-scans: jobs: - secops/gitleaks: @@ -121,3 +184,8 @@ workflows: - secops-oidc git-base-revision: <<#pipeline.git.base_revision>><><> git-revision: << pipeline.git.revision >> + - secops/semgrep: + context: + - secops-oidc + - github-orb + git-base-revision: <<#pipeline.git.base_revision>><><> diff --git a/.eslintrc b/.eslintrc index dccf775414b..b4d6d6f5363 100644 --- a/.eslintrc +++ b/.eslintrc @@ -23,10 +23,13 @@ { "files": ["**/*.ts", "**/*.tsx"], "excludedFiles": ["**/__tests__/**/*.*", "*.d.ts"], + "extends": ["plugin:react-hooks/recommended"], "parserOptions": { "project": "./tsconfig.json" }, + "plugins": ["eslint-plugin-react-compiler"], "rules": { + "react-compiler/react-compiler": "error", "@typescript-eslint/consistent-type-imports": [ "error", { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fd44e4c9fc9..866b0e4ba26 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,3 @@ +* @apollographql/client-typescript + /docs/ @apollographql/docs diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d2e1bc647dc..618626d418f 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -22,3 +22,10 @@ body: description: Please provide any additional non-trivial steps required to reproduce the issue. validations: required: false + - type: input + attributes: + label: "`@apollo/client` version" + description: "What version of Apollo Client are you running?" + placeholder: "ex: 3.8.10" + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1ae6e9441ea..fa7f8ca35f5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,10 +16,9 @@ Please look at the following checklist to ensure that your PR can be accepted quickly: ---> ### Checklist: - - [ ] If this PR contains changes to the library itself (not necessary for e.g. docs updates), please include a changeset (see [CONTRIBUTING.md](https://github.com/apollographql/apollo-client/blob/main/CONTRIBUTING.md#changesets)) - [ ] If this PR is a new feature, please reference an issue where a consensus about the design was reached (not necessary for small changes) - [ ] Make sure all of the significant new logic is covered by tests +--> diff --git a/.github/workflows/api-extractor.yml b/.github/workflows/api-extractor.yml index aa23fc958a5..b54d5e078aa 100644 --- a/.github/workflows/api-extractor.yml +++ b/.github/workflows/api-extractor.yml @@ -11,16 +11,14 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 - - name: Run build - run: npm run build - + # Builds the library and runs the api extractor - name: Run Api-Extractor run: npm run extract-api diff --git a/.github/workflows/arethetypeswrong.yml b/.github/workflows/arethetypeswrong.yml index fb9c4768c32..c83c623241f 100644 --- a/.github/workflows/arethetypeswrong.yml +++ b/.github/workflows/arethetypeswrong.yml @@ -14,10 +14,10 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v4 - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 diff --git a/.github/workflows/change-prerelease-tag.yml b/.github/workflows/change-prerelease-tag.yml new file mode 100644 index 00000000000..208f10d0b57 --- /dev/null +++ b/.github/workflows/change-prerelease-tag.yml @@ -0,0 +1,69 @@ +name: Change Prerelease Tag + +on: + workflow_dispatch: + inputs: + branch: + description: "Branch name" + type: string + default: "release-" + required: true + tag: + description: "New tag name" + type: string + default: "rc" + required: true + +jobs: + change_prerelease_tag: + name: Changesets Update Prerelease Tag + runs-on: ubuntu-latest + # Allow GITHUB_TOKEN to have write permissions + permissions: + contents: write + + steps: + - uses: actions/create-github-app-token@v1 + id: github-actions-bot-app-token + with: + app-id: 819772 + private-key: ${{ secrets.APOLLO_GITHUB_ACTIONS_BOT_PRIVATE_KEY }} + + # Check out the repository, using the Github Actions Bot app's token so + # that we can push later. + - name: Checkout repo + uses: actions/checkout@v4 + with: + token: ${{ steps.github-actions-bot-app-token.outputs.token }} + # Checkout release branch entered when workflow was kicked off + ref: ${{ github.event.inputs.branch }} + # Fetch entire git history so Changesets can generate changelogs + # with the correct commits + fetch-depth: 0 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + + - name: Write latest version to package.json and package-lock.json + run: | + version=$(npm show @apollo/client version) + npm pkg set version="$version" + npm i + + - name: Update prerelease tag in .changeset/pre.json + uses: restackio/update-json-file-action@2.1 + with: + file: .changeset/pre.json + fields: '{"tag": "${{github.event.inputs.tag}}"}' + + - name: Commit and push changes + env: + TAG: ${{ github.event.inputs.tag }} + run: | + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + git add -A + git commit -m "Prepare for "$TAG" release" + git push diff --git a/.github/workflows/cleanup-checks.yml b/.github/workflows/cleanup-checks.yml new file mode 100644 index 00000000000..132f64ab792 --- /dev/null +++ b/.github/workflows/cleanup-checks.yml @@ -0,0 +1,60 @@ +name: Clean up Prettier, Size-limit, and Api-Extractor + +on: + pull_request: + pull_request_review: + types: [submitted, edited] + +jobs: + add_cleanup_label: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-a-workflow-when-a-pull-request-is-approved + if: | + github.repository == 'apollographql/apollo-client' && + github.event.review.state == 'APPROVED' && + contains(github.event.pull_request.labels.*.name, 'auto-cleanup') == false + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: add label + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: auto-cleanup + + cleanup: + if: | + github.repository == 'apollographql/apollo-client' && + contains(github.event.pull_request.labels.*.name, 'auto-cleanup') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + + - name: Install dependencies (with cache) + uses: bahmutov/npm-install@v1 + + - name: Run build + run: npm run build + + - name: Run Api-Extractor + run: npm run extract-api + env: + CI: "false" + + - name: Run prettier + run: npm run format + + - name: Update size-limit + run: npm run update-size-limits + + - name: Commit changes back + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Clean up Prettier, Size-limit, and Api-Extractor" + push_options: "" + skip_dirty_check: false diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 5d54d58ea6d..55a67e4ae69 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Close Stale Issues - uses: actions/stale@v8.0.0 + uses: actions/stale@v9.0.0 with: # # Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`. # repo-token: # optional, default is ${{ github.token }} diff --git a/.github/workflows/compare-build-output.yml b/.github/workflows/compare-build-output.yml index dedf208c41e..7b97c556665 100644 --- a/.github/workflows/compare-build-output.yml +++ b/.github/workflows/compare-build-output.yml @@ -17,10 +17,10 @@ jobs: with: # Fetch entire git history so we have the parent commit to compare against fetch-depth: 0 - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 diff --git a/.github/workflows/devtools-errorcodes.yml b/.github/workflows/devtools-errorcodes.yml new file mode 100644 index 00000000000..747b5cc236f --- /dev/null +++ b/.github/workflows/devtools-errorcodes.yml @@ -0,0 +1,22 @@ +name: Devtools - Trigger Error Code PR after npm Release +on: + workflow_dispatch: # for testing + workflow_run: + workflows: ["Prerelease", "Release"] + types: + - completed +jobs: + dispatch: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@v1 + id: github-actions-bot-app-token + with: + app-id: 819772 + private-key: ${{ secrets.APOLLO_GITHUB_ACTIONS_BOT_PRIVATE_KEY }} + repositories: apollo-client-devtools + - uses: benc-uk/workflow-dispatch@v1 + with: + workflow: update-errorcodes.yml + repo: apollographql/apollo-client-devtools + token: ${{ steps.github-actions-bot-app-token.outputs.token }} diff --git a/.github/workflows/exit-prerelease.yml b/.github/workflows/exit-prerelease.yml index a96c1781b1c..6ac4e76b1a8 100644 --- a/.github/workflows/exit-prerelease.yml +++ b/.github/workflows/exit-prerelease.yml @@ -18,42 +18,42 @@ jobs: contents: write steps: + - uses: actions/create-github-app-token@v1 + id: github-actions-bot-app-token + with: + app-id: 819772 + private-key: ${{ secrets.APOLLO_GITHUB_ACTIONS_BOT_PRIVATE_KEY }} + + # Check out the repository, using the Github Actions Bot app's token so + # that we can push later. - name: Checkout repo uses: actions/checkout@v4 with: + token: ${{ steps.github-actions-bot-app-token.outputs.token }} # Checkout release branch entered when workflow was kicked off ref: ${{ github.event.inputs.branch }} # Fetch entire git history so Changesets can generate changelogs # with the correct commits fetch-depth: 0 - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - - name: Get latest tagged version - id: previoustag - uses: WyriHaximus/github-action-get-previous-tag@v1 - - - name: Remove 'v' prefix from version number (e.g. v1.0.0) - uses: mad9000/actions-find-and-replace-string@4 - id: formatversion - with: - source: ${{ steps.previoustag.outputs.tag }} - find: "v" - replace: "" - - - name: Write previous version to package.json - uses: jaywcjlove/github-action-package@v1.3.1 - with: - version: ${{ steps.formatversion.outputs.value }} + - name: Write latest version to package.json and package-lock.json + run: | + version=$(npm show @apollo/client version) + npm pkg set version="$version" + npm i - name: Remove pre.json run: npx rimraf .changeset/pre.json - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: Exit prerelease mode - # Commit these changes to the branch workflow is running against - branch: ${{ github.event.inputs.branch }} + - name: Commit and push changes + run: | + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + git add -A + git commit -m "Exit prerelease mode" + git push diff --git a/.github/workflows/issue-close-user-survey.yml b/.github/workflows/issue-close-user-survey.yml new file mode 100644 index 00000000000..36b18773fc6 --- /dev/null +++ b/.github/workflows/issue-close-user-survey.yml @@ -0,0 +1,27 @@ +name: Issue Close User Survey + +on: + issues: + types: [closed] + +jobs: + user-survey-comment: + permissions: + issues: write + runs-on: ubuntu-latest + if: github.repository == 'apollographql/apollo-client' + steps: + - run: | + if [ "$STATE_REASON" == "completed" ] || [ "$SENDER" != "github-actions" ]; then + gh issue comment "$NUMBER" --body "$BODY" + else + echo "Issue was closed as not planned, skipping comment." + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + STATE_REASON: ${{ github.event.issue.state_reason }} + SENDER: ${{ github.event.sender.login }} + BODY: > + Do you have any feedback for the maintainers? Please tell us by taking a [one-minute survey](https://docs.google.com/forms/d/e/1FAIpQLSczNDXfJne3ZUOXjk9Ursm9JYvhTh1_nFTDfdq3XBAFWCzplQ/viewform?usp=pp_url&entry.1170701325=Apollo+Client&entry.204965213=GitHub+Issue). Your responses will help us understand Apollo Client usage and allow us to serve you better. diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 8c050ae5b5e..5cc733c8cd7 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -16,7 +16,7 @@ jobs: if: github.repository == 'apollographql/apollo-client' runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} log-output: true diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index c2907fae17a..586a3477e61 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -22,9 +22,18 @@ jobs: pull-requests: write id-token: write steps: + - uses: actions/create-github-app-token@v1 + id: github-actions-bot-app-token + with: + app-id: 819772 + private-key: ${{ secrets.APOLLO_GITHUB_ACTIONS_BOT_PRIVATE_KEY }} + + # Check out the repository, using the Github Actions Bot app's token so + # that we can push later. - name: Checkout repo uses: actions/checkout@v4 with: + token: ${{ steps.github-actions-bot-app-token.outputs.token }} # Fetch entire git history so Changesets can generate changelogs # with the correct commits fetch-depth: 0 @@ -38,17 +47,17 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies with cache uses: bahmutov/npm-install@v1 - name: Check for pre.json file existence id: check_files - uses: andstor/file-existence-action@v2.0.0 + uses: andstor/file-existence-action@v3.0.0 with: files: ".changeset/pre.json" @@ -56,9 +65,33 @@ jobs: # If .changeset/pre.json does not exist and we did not recently exit # prerelease mode, enter prerelease mode with tag alpha if: steps.check_files.outputs.files_exists == 'false' && !contains(github.event.head_commit.message, 'Exit prerelease') - run: npx changeset pre enter alpha + run: | + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + npx changeset pre enter alpha + git add -A + git commit -m 'Enter prerelease mode' + git push + + - name: Get prerelease changesets + id: prerelease-changesets + uses: notiz-dev/github-action-json-property@release + with: + path: ".changeset/pre.json" + prop_path: "changesets" + + - name: Get changesets array length + id: number-of-changesets + run: | + arrayLength=$(echo '${{steps.prerelease-changesets.outputs.prop}}' | jq '. | length') + echo "length=$arrayLength" >> "$GITHUB_OUTPUT" - name: Create prerelease PR + # Only attempt to create a PR if: + # 1. .changeset/pre.json exists + # 2. we are not actively publishing after merging a Version Packages PR + # 3. AND we have prerelease changesets to publish (otherwise it errors) + if: steps.check_files.outputs.files_exists == 'true' && !startsWith(github.event.head_commit.message, 'Version Packages') && steps.number-of-changesets.outputs.length > 0 uses: changesets/action@v1 with: version: npm run changeset-version @@ -67,8 +100,8 @@ jobs: - name: Publish to npm + GitHub id: changesets - # Only run publish if we're still in pre mode and the last commit was - # via an automatically created Version Packages PR + # Only publish if we're still in pre mode and the last commit was + # from an automatically created Version Packages PR if: steps.check_files.outputs.files_exists == 'true' && startsWith(github.event.head_commit.message, 'Version Packages') uses: changesets/action@v1 with: @@ -85,7 +118,7 @@ jobs: - name: Send a Slack notification on publish if: steps.changesets.outcome == 'success' id: slack - uses: slackapi/slack-github-action@v1.24.0 + uses: slackapi/slack-github-action@v1.26.0 with: # Slack channel id, channel name, or user id to post message # See also: https://api.slack.com/methods/chat.postMessage#channels diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e71d47e5a7d..61d0c6676e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: - name: Check for pre.json file existence id: check_files - uses: andstor/file-existence-action@v2.0.0 + uses: andstor/file-existence-action@v3.0.0 with: files: ".changeset/pre.json" @@ -43,10 +43,10 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 @@ -65,7 +65,7 @@ jobs: - name: Send a Slack notification on publish if: steps.changesets.outcome == 'success' && steps.changesets.outputs.published == 'true' id: slack - uses: slackapi/slack-github-action@v1.24.0 + uses: slackapi/slack-github-action@v1.26.0 with: # Slack channel id, channel name, or user id to post message # See also: https://api.slack.com/methods/chat.postMessage#channels diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 249c61c98ff..2295fc46951 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -11,10 +11,10 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v4 - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 - name: Run size-limit diff --git a/.github/workflows/snapshot-release.yml b/.github/workflows/snapshot-release.yml index 894421db6c7..d38a265b3fe 100644 --- a/.github/workflows/snapshot-release.yml +++ b/.github/workflows/snapshot-release.yml @@ -30,22 +30,56 @@ jobs: startsWith(github.event.comment.body, '/release:pr') steps: - - uses: alessbell/pull-request-comment-branch@v1.1 + - uses: alessbell/pull-request-comment-branch@v2.1.0 id: comment-branch - - name: Checkout head ref + - name: Get sha + id: parse-sha + continue-on-error: true + run: | + if [ "${{ steps.comment-branch.outputs.head_owner }}" == "apollographql" ]; then + echo "sha=${{ steps.comment-branch.outputs.head_sha }}" >> "${GITHUB_OUTPUT}" + else + sha_from_comment="$(echo $COMMENT_BODY | tr -s ' ' | cut -d ' ' -f2)" + + if [ $sha_from_comment == "/release:pr" ]; then + exit 1 + else + echo "sha=$sha_from_comment" >> "${GITHUB_OUTPUT}" + fi + fi + env: + COMMENT_BODY: ${{ github.event.comment.body }} + + - name: Comment sha reminder + if: steps.parse-sha.outcome == 'failure' + uses: peter-evans/create-or-update-comment@v4.0.0 + with: + issue-number: ${{ github.event.issue.number }} + body: | + Did you forget to add the sha? Please use `/release:pr ` + + - name: Fail job + if: steps.parse-sha.outcome == 'failure' + run: | + exit 1 + + - name: Checkout ref uses: actions/checkout@v4 with: ## specify the owner + repository in order to checkout the fork ## for community PRs repository: ${{ steps.comment-branch.outputs.head_owner }}/${{ steps.comment-branch.outputs.head_repo }} - ref: ${{ steps.comment-branch.outputs.head_ref }} + ref: ${{ steps.parse-sha.outputs.sha }} fetch-depth: 0 - name: Detect new changesets id: added-files run: | - echo "changesets=$(git diff --name-only --diff-filter=A ${{ steps.comment-branch.outputs.base_sha }} ${{ steps.comment-branch.outputs.head_sha }} .changeset/*.md)" >> "$GITHUB_OUTPUT" + delimiter="$(openssl rand -hex 8)" + echo "changesets<<${delimiter}" >> "${GITHUB_OUTPUT}" + echo "$(git diff --name-only --diff-filter=A ${{ steps.comment-branch.outputs.base_sha }} ${{ steps.parse-sha.outputs.sha }} .changeset/*.md)" >> "${GITHUB_OUTPUT}" + echo "${delimiter}" >> "${GITHUB_OUTPUT}" - name: Append NPM token to .npmrc run: | @@ -56,17 +90,17 @@ jobs: env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Setup Node.js 18.x + - name: Setup Node.js 20.x uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - name: Install dependencies (with cache) uses: bahmutov/npm-install@v1 - name: Check for pre.json file existence id: check_files - uses: andstor/file-existence-action@v2.0.0 + uses: andstor/file-existence-action@v3.0.0 with: files: ".changeset/pre.json" @@ -80,7 +114,7 @@ jobs: - name: Add comment if no new changesets exist if: ${{ steps.added-files.outputs.changesets == '' }} - uses: peter-evans/create-or-update-comment@v3.1.0 + uses: peter-evans/create-or-update-comment@v4.0.0 with: issue-number: ${{ github.event.issue.number }} body: | @@ -106,8 +140,12 @@ jobs: - name: Create comment if: ${{ steps.added-files.outputs.changesets != '' }} - uses: peter-evans/create-or-update-comment@v3.1.0 + uses: peter-evans/create-or-update-comment@v4.0.0 with: issue-number: ${{ github.event.issue.number }} body: | - A new release has been made for this PR. You can install it with `npm i @apollo/client@${{ steps.get-version.outputs.version }}`. + A new release has been made for this PR. You can install it with: + + ``` + npm i @apollo/client@${{ steps.get-version.outputs.version }} + ``` diff --git a/.prettierignore b/.prettierignore index 5c9398e78de..4af59c9d031 100644 --- a/.prettierignore +++ b/.prettierignore @@ -24,7 +24,16 @@ !/docs/source/development-testing /docs/source/development-testing/** !/docs/source/development-testing/reducing-bundle-size.mdx +!/docs/source/development-testing/schema-driven-testing.mdx + +!docs/shared +/docs/shared/** +!/docs/shared/ApiDoc +!/docs/shared/ApiDoc/** +!/docs/shared/Overrides +!/docs/shared/Overrides/** node_modules/ .yalc/ .next/ +.changeset/ diff --git a/.prettierrc b/.prettierrc index c21d016783a..e2511027712 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "semi": true, "singleQuote": false, "tabWidth": 2, - "trailingComma": "es5" + "trailingComma": "es5", + "experimentalTernaries": true } diff --git a/.size-limit.cjs b/.size-limit.cjs index d63784ccf10..a91cfa60a04 100644 --- a/.size-limit.cjs +++ b/.size-limit.cjs @@ -1,7 +1,8 @@ +const limits = require("./.size-limits.json"); + const checks = [ { path: "dist/apollo-client.min.cjs", - limit: "37956", }, { path: "dist/main.cjs", @@ -10,7 +11,6 @@ const checks = [ { path: "dist/index.js", import: "{ ApolloClient, InMemoryCache, HttpLink }", - limit: "32017", }, ...[ "ApolloProvider", @@ -20,6 +20,7 @@ const checks = [ "useSubscription", "useSuspenseQuery", "useBackgroundQuery", + "useLoadableQuery", "useReadQuery", "useFragment", ].map((name) => ({ path: "dist/react/index.js", import: `{ ${name} }` })), @@ -27,14 +28,19 @@ const checks = [ .map((config) => ({ ...config, name: - config.name || config.import - ? `import ${config.import} from "${config.path}"` - : config.path, + config.name || config.import ? + `import ${config.import} from "${config.path}"` + : config.path, + // newer versions of size-limit changed to brotli as a default + // we'll stay on gzip for now, so results are easier to compare + gzip: true, ignore: [ ...(config.ignore || []), + "rehackt", "react", "react-dom", "@graphql-typed-document-node/core", + "@wry/caches", "@wry/context", "@wry/equality", "@wry/trie", @@ -50,21 +56,25 @@ const checks = [ ], })) .flatMap((value) => - value.path == "dist/apollo-client.min.cjs" - ? value - : [ - { ...value, limit: undefined }, - { - ...value, - name: `${value.name} (production)`, - modifyEsbuildConfig(config) { - config.define = { - "globalThis.__DEV__": `false`, - }; - return config; - }, + value.path == "dist/apollo-client.min.cjs" ? + value + : [ + value, + { + ...value, + name: `${value.name} (production)`, + modifyEsbuildConfig(config) { + config.define = { + "globalThis.__DEV__": `false`, + }; + return config; }, - ] - ); + }, + ] + ) + .map((value) => { + value.limit = limits[value.name]; + return value; + }); module.exports = checks; diff --git a/.size-limits.json b/.size-limits.json new file mode 100644 index 00000000000..5c28672b1f7 --- /dev/null +++ b/.size-limits.json @@ -0,0 +1,4 @@ +{ + "dist/apollo-client.min.cjs": 39561, + "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32821 +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 6cdb71bc99d..d40a6ff9315 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,25 +1,22 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Attach to Node.js inspector", - "port": 9229, - "request": "attach", - "skipFiles": ["/**"], - "type": "pwa-node" - }, { "type": "node", "request": "launch", - "name": "Jest Current File", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": ["${relativeFile}", "--config", "./config/jest.config.js"], + "name": "Jest Attach Node Inspector for Current File", + "cwd": "${workspaceFolder}", + "runtimeArgs": [ + "--inspect-brk", + "${workspaceRoot}/node_modules/.bin/jest", + "${relativeFile}", + "--config", + "./config/jest.config.js", + "--runInBand", + "--watch" + ], "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true, - "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest" - } + "internalConsoleOptions": "neverOpen" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index fa43468947b..e4473180110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,586 @@ # @apollo/client +## 3.10.4 + +### Patch Changes + +- [#11838](https://github.com/apollographql/apollo-client/pull/11838) [`8475346`](https://github.com/apollographql/apollo-client/commit/84753462af50d89c8693713990cccf432ff8267d) Thanks [@alex-kinokon](https://github.com/alex-kinokon)! - Don’t prompt for DevTools installation for browser extension page + +- [#11839](https://github.com/apollographql/apollo-client/pull/11839) [`6481fe1`](https://github.com/apollographql/apollo-client/commit/6481fe1196cedee987781dcb45ebdc0cafb3998c) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix a regression in [3.9.5](https://github.com/apollographql/apollo-client/releases/tag/v3.9.5) where a merge function that returned an incomplete result would not allow the client to refetch in order to fulfill the query. + +- [#11844](https://github.com/apollographql/apollo-client/pull/11844) [`86984f2`](https://github.com/apollographql/apollo-client/commit/86984f24bd9076a6034acd59bbcb28a2ea1add93) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Honor the `@nonreactive` directive when using `cache.watchFragment` or the `useFragment` hook to avoid rerendering when using these directives. + +- [#11824](https://github.com/apollographql/apollo-client/pull/11824) [`47ad806`](https://github.com/apollographql/apollo-client/commit/47ad806c7b0c55f1e05dbf276ca87a354ac389e5) Thanks [@phryneas](https://github.com/phryneas)! - Create branded `QueryRef` type without exposed properties. + + This change deprecates `QueryReference` in favor of a `QueryRef` type that doesn't expose any properties. + This change also updates `preloadQuery` to return a new `PreloadedQueryRef` type, which exposes the `toPromise` function as it does today. This means that query refs produced by `useBackgroundQuery` and `useLoadableQuery` now return `QueryRef` types that do not have access to a `toPromise` function, which was never meant to be used in combination with these hooks. + + While we tend to avoid any types of breaking changes in patch releases as this, this change was necessary to support an upcoming version of the React Server Component integration, which needed to omit the `toPromise` function that would otherwise have broken at runtime. + Note that this is a TypeScript-only change. At runtime, `toPromise` is still present on all queryRefs currently created by this package - but we strongly want to discourage you from accessing it in all cases except for the `PreloadedQueryRef` use case. + + Migration is as simple as replacing all references to `QueryReference` with `QueryRef`, so it should be possible to do this with a search & replace in most code bases: + + ```diff + -import { QueryReference } from '@apollo/client' + +import { QueryRef } from '@apollo/client' + + - function Component({ queryRef }: { queryRef: QueryReference }) { + + function Component({ queryRef }: { queryRef: QueryRef }) { + // ... + } + ``` + +- [#11845](https://github.com/apollographql/apollo-client/pull/11845) [`4c5c820`](https://github.com/apollographql/apollo-client/commit/4c5c820b6172f6a2455bcdd974109513e0e2a39e) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove `@nonreactive` directives from queries passed to `MockLink` to ensure they are properly matched. + +- [#11837](https://github.com/apollographql/apollo-client/pull/11837) [`dff15b1`](https://github.com/apollographql/apollo-client/commit/dff15b1b03ebac9cae508c69bf607a29d0f6eccb) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a polled query created in React strict mode may not stop polling after the component unmounts while using the `cache-and-network` fetch policy. + +## 3.10.3 + +### Patch Changes + +- [#11811](https://github.com/apollographql/apollo-client/pull/11811) [`d67d7f9`](https://github.com/apollographql/apollo-client/commit/d67d7f9a2943273cacaefb26a54184e81f12b022) Thanks [@phryneas](https://github.com/phryneas)! - Adjust some types for React 19 compat + +- [#11834](https://github.com/apollographql/apollo-client/pull/11834) [`7d8aad4`](https://github.com/apollographql/apollo-client/commit/7d8aad4a00b89e0208ee1563293c24025e6604ce) Thanks [@psamim](https://github.com/psamim)! - Fix error "Cannot convert object to primitive value" + +## 3.10.2 + +### Patch Changes + +- [#11821](https://github.com/apollographql/apollo-client/pull/11821) [`2675d3c`](https://github.com/apollographql/apollo-client/commit/2675d3c97e6c47c6e298382004c7c9c2d3ffed0c) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix a regression where rerendering a component with `useBackgroundQuery` would recreate the `queryRef` instance when used with React's strict mode. + +- [#11821](https://github.com/apollographql/apollo-client/pull/11821) [`2675d3c`](https://github.com/apollographql/apollo-client/commit/2675d3c97e6c47c6e298382004c7c9c2d3ffed0c) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Revert the change introduced in + [3.9.10](https://github.com/apollographql/apollo-client/releases/tag/v3.9.10) via #11738 that disposed of queryRefs synchronously. This change caused too many issues with strict mode. + +## 3.10.1 + +### Patch Changes + +- [#11792](https://github.com/apollographql/apollo-client/pull/11792) [`5876c35`](https://github.com/apollographql/apollo-client/commit/5876c35530a21473207954d1f0c2b7dd00c0b9ea) Thanks [@phryneas](https://github.com/phryneas)! - AutoCleanedCache: only schedule batched cache cleanup if the cache is full (fixes #11790) + +- [#11799](https://github.com/apollographql/apollo-client/pull/11799) [`1aca7ed`](https://github.com/apollographql/apollo-client/commit/1aca7ed5a3accf2303ccdf9b3dece7278f03ad62) Thanks [@phryneas](https://github.com/phryneas)! - `RenderPromises`: use `canonicalStringify` to serialize `variables` to ensure query deduplication is properly applied even when `variables` are specified in a different order. + +- [#11803](https://github.com/apollographql/apollo-client/pull/11803) [`bf9dd17`](https://github.com/apollographql/apollo-client/commit/bf9dd17b288f33901e9421bcc0eacb3894c087af) Thanks [@phryneas](https://github.com/phryneas)! - Update the `rehackt` dependency to `^0.1.0` + +- [#11756](https://github.com/apollographql/apollo-client/pull/11756) [`60592e9`](https://github.com/apollographql/apollo-client/commit/60592e95399c3695d1d49a4c39ad29f00d4059fd) Thanks [@henryqdineen](https://github.com/henryqdineen)! - Fix operation.setContext() type + +## 3.10.0 + +### Minor Changes + +- [#11605](https://github.com/apollographql/apollo-client/pull/11605) [`e2dd4c9`](https://github.com/apollographql/apollo-client/commit/e2dd4c95290cea604b548cc446826d89aafe8e11) Thanks [@alessbell](https://github.com/alessbell)! - Adds `createMockFetch` utility for integration testing that includes the link chain + +- [#11760](https://github.com/apollographql/apollo-client/pull/11760) [`acd1982`](https://github.com/apollographql/apollo-client/commit/acd1982a59ed66fc44fa9e70b08a31c69dac35a6) Thanks [@alessbell](https://github.com/alessbell)! - `createTestSchema` now uses graphql-tools `mergeResolvers` to merge resolvers instead of a shallow merge. + +- [#11764](https://github.com/apollographql/apollo-client/pull/11764) [`f046aa9`](https://github.com/apollographql/apollo-client/commit/f046aa9fc24ac197a797045d280811a3bbe05806) Thanks [@alessbell](https://github.com/alessbell)! - Rename `createProxiedSchema` to `createTestSchema` and `createMockFetch` to `createSchemaFetch`. + +- [#11777](https://github.com/apollographql/apollo-client/pull/11777) [`5dfc79f`](https://github.com/apollographql/apollo-client/commit/5dfc79fa6d974362f38361f7dffbe984a9546377) Thanks [@alessbell](https://github.com/alessbell)! - Call `createMockSchema` inside `createTestSchema`. + +- [#11774](https://github.com/apollographql/apollo-client/pull/11774) [`2583488`](https://github.com/apollographql/apollo-client/commit/2583488677912cb4500e5fb9e3f91b5c113c4cdb) Thanks [@alessbell](https://github.com/alessbell)! - Add ability to set min and max delay in `createSchemaFetch` + +- [#11605](https://github.com/apollographql/apollo-client/pull/11605) [`e2dd4c9`](https://github.com/apollographql/apollo-client/commit/e2dd4c95290cea604b548cc446826d89aafe8e11) Thanks [@alessbell](https://github.com/alessbell)! - Adds proxiedSchema and createMockSchema testing utilities + +- [#11465](https://github.com/apollographql/apollo-client/pull/11465) [`7623da7`](https://github.com/apollographql/apollo-client/commit/7623da7720855b0c19e13ff9124679f426a39725) Thanks [@alessbell](https://github.com/alessbell)! - Add `watchFragment` method to the cache and expose it on ApolloClient, refactor `useFragment` using `watchFragment`. + +- [#11743](https://github.com/apollographql/apollo-client/pull/11743) [`78891f9`](https://github.com/apollographql/apollo-client/commit/78891f9ec81c0b7a7e010f5550a91965fa33a958) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove alpha designation for `queryRef.toPromise()` to stabilize the API. + +- [#11743](https://github.com/apollographql/apollo-client/pull/11743) [`78891f9`](https://github.com/apollographql/apollo-client/commit/78891f9ec81c0b7a7e010f5550a91965fa33a958) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove alpha designation for `createQueryPreloader` to stabilize the API. + +- [#11783](https://github.com/apollographql/apollo-client/pull/11783) [`440563a`](https://github.com/apollographql/apollo-client/commit/440563ab2c47efcb9c7d08f52531ade33d753037) Thanks [@alessbell](https://github.com/alessbell)! - Moves new testing utilities to their own entrypoint, `testing/experimental` + +### Patch Changes + +- [#11757](https://github.com/apollographql/apollo-client/pull/11757) [`9825295`](https://github.com/apollographql/apollo-client/commit/982529530893f66a1d236f0fff53862e513fc9a8) Thanks [@phryneas](https://github.com/phryneas)! - Adjust `useReadQuery` wrapper logic to work with transported objects. + +- [#11771](https://github.com/apollographql/apollo-client/pull/11771) [`e72cbba`](https://github.com/apollographql/apollo-client/commit/e72cbba07e5caa6d75b44ca8c766846e855a6c93) Thanks [@phryneas](https://github.com/phryneas)! - Wrap `useQueryRefHandlers` in `wrapHook`. + +- [#11754](https://github.com/apollographql/apollo-client/pull/11754) [`80d2ba5`](https://github.com/apollographql/apollo-client/commit/80d2ba579fe6d2a2d102d1fe79d7d503f31cd931) Thanks [@alessbell](https://github.com/alessbell)! - Export `WatchFragmentOptions` and `WatchFragmentResult` from main entrypoint and fix bug where `this` wasn't bound to the `watchFragment` method on `ApolloClient`. + +## 3.10.0-rc.1 + +### Minor Changes + +- [#11760](https://github.com/apollographql/apollo-client/pull/11760) [`acd1982`](https://github.com/apollographql/apollo-client/commit/acd1982a59ed66fc44fa9e70b08a31c69dac35a6) Thanks [@alessbell](https://github.com/alessbell)! - `createTestSchema` now uses graphql-tools `mergeResolvers` to merge resolvers instead of a shallow merge. + +- [#11764](https://github.com/apollographql/apollo-client/pull/11764) [`f046aa9`](https://github.com/apollographql/apollo-client/commit/f046aa9fc24ac197a797045d280811a3bbe05806) Thanks [@alessbell](https://github.com/alessbell)! - Rename `createProxiedSchema` to `createTestSchema` and `createMockFetch` to `createSchemaFetch`. + +- [#11777](https://github.com/apollographql/apollo-client/pull/11777) [`5dfc79f`](https://github.com/apollographql/apollo-client/commit/5dfc79fa6d974362f38361f7dffbe984a9546377) Thanks [@alessbell](https://github.com/alessbell)! - Call `createMockSchema` inside `createTestSchema`. + +- [#11774](https://github.com/apollographql/apollo-client/pull/11774) [`2583488`](https://github.com/apollographql/apollo-client/commit/2583488677912cb4500e5fb9e3f91b5c113c4cdb) Thanks [@alessbell](https://github.com/alessbell)! - Add ability to set min and max delay in `createSchemaFetch` + +- [#11783](https://github.com/apollographql/apollo-client/pull/11783) [`440563a`](https://github.com/apollographql/apollo-client/commit/440563ab2c47efcb9c7d08f52531ade33d753037) Thanks [@alessbell](https://github.com/alessbell)! - Moves new testing utilities to their own entrypoint, `testing/experimental` + +### Patch Changes + +- [#11757](https://github.com/apollographql/apollo-client/pull/11757) [`9825295`](https://github.com/apollographql/apollo-client/commit/982529530893f66a1d236f0fff53862e513fc9a8) Thanks [@phryneas](https://github.com/phryneas)! - Adjust `useReadQuery` wrapper logic to work with transported objects. + +- [#11771](https://github.com/apollographql/apollo-client/pull/11771) [`e72cbba`](https://github.com/apollographql/apollo-client/commit/e72cbba07e5caa6d75b44ca8c766846e855a6c93) Thanks [@phryneas](https://github.com/phryneas)! - Wrap `useQueryRefHandlers` in `wrapHook`. + +- [#11754](https://github.com/apollographql/apollo-client/pull/11754) [`80d2ba5`](https://github.com/apollographql/apollo-client/commit/80d2ba579fe6d2a2d102d1fe79d7d503f31cd931) Thanks [@alessbell](https://github.com/alessbell)! - Export `WatchFragmentOptions` and `WatchFragmentResult` from main entrypoint and fix bug where `this` wasn't bound to the `watchFragment` method on `ApolloClient`. + +## 3.10.0-rc.0 + +### Minor Changes + +- [#11605](https://github.com/apollographql/apollo-client/pull/11605) [`e2dd4c9`](https://github.com/apollographql/apollo-client/commit/e2dd4c95290cea604b548cc446826d89aafe8e11) Thanks [@alessbell](https://github.com/alessbell)! - Adds `createMockFetch` utility for integration testing that includes the link chain + +- [#11605](https://github.com/apollographql/apollo-client/pull/11605) [`e2dd4c9`](https://github.com/apollographql/apollo-client/commit/e2dd4c95290cea604b548cc446826d89aafe8e11) Thanks [@alessbell](https://github.com/alessbell)! - Adds proxiedSchema and createMockSchema testing utilities + +- [#11743](https://github.com/apollographql/apollo-client/pull/11743) [`78891f9`](https://github.com/apollographql/apollo-client/commit/78891f9ec81c0b7a7e010f5550a91965fa33a958) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove alpha designation for `queryRef.toPromise()` to stabilize the API. + +- [#11743](https://github.com/apollographql/apollo-client/pull/11743) [`78891f9`](https://github.com/apollographql/apollo-client/commit/78891f9ec81c0b7a7e010f5550a91965fa33a958) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove alpha designation for `createQueryPreloader` to stabilize the API. + +## 3.10.0-alpha.0 + +### Minor Changes + +- [#11465](https://github.com/apollographql/apollo-client/pull/11465) [`7623da7`](https://github.com/apollographql/apollo-client/commit/7623da7720855b0c19e13ff9124679f426a39725) Thanks [@alessbell](https://github.com/alessbell)! - Add `watchFragment` method to the cache and expose it on ApolloClient, refactor `useFragment` using `watchFragment`. + +## 3.9.11 + +### Patch Changes + +- [#11769](https://github.com/apollographql/apollo-client/pull/11769) [`04132af`](https://github.com/apollographql/apollo-client/commit/04132af121c9b48d6e03eb733b9b91f825defbac) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where using `skipToken` or the `skip` option with `useSuspenseQuery` in React's strict mode would perform a network request. + +## 3.9.10 + +### Patch Changes + +- [#11738](https://github.com/apollographql/apollo-client/pull/11738) [`b1a5eb8`](https://github.com/apollographql/apollo-client/commit/b1a5eb80cae8bdf2e9d8627f1eab65e088c43438) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where rerendering `useBackgroundQuery` after the `queryRef` had been disposed, either via the auto dispose timeout or by unmounting `useReadQuery`, would cause the `queryRef` to be recreated potentially resulting in another network request. + +- [#11738](https://github.com/apollographql/apollo-client/pull/11738) [`b1a5eb8`](https://github.com/apollographql/apollo-client/commit/b1a5eb80cae8bdf2e9d8627f1eab65e088c43438) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Allow queryRefs to be disposed of synchronously when a suspense hook unmounts. This prevents some situations where using a suspense hook with the same query/variables as the disposed queryRef accidentally used the disposed queryRef rather than creating a new instance. + +- [#11670](https://github.com/apollographql/apollo-client/pull/11670) [`cc5c03b`](https://github.com/apollographql/apollo-client/commit/cc5c03b2690f452483d83eecb68611a23055d99e) Thanks [@phryneas](https://github.com/phryneas)! - Bail out of `executeSubSelectedArray` calls if the array has 0 elements. + +## 3.9.9 + +### Patch Changes + +- [#11696](https://github.com/apollographql/apollo-client/pull/11696) [`466ef82`](https://github.com/apollographql/apollo-client/commit/466ef82198486fc696da64d17d82b46140760ac4) Thanks [@PiR1](https://github.com/PiR1)! - Immediately dispose of the `queryRef` if `useBackgroundQuery` unmounts before the auto dispose timeout kicks in. + +## 3.9.8 + +### Patch Changes + +- [#11706](https://github.com/apollographql/apollo-client/pull/11706) [`8619bc7`](https://github.com/apollographql/apollo-client/commit/8619bc7e569c1c732afa6faf605c83a6ce0cdf0c) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue in all suspense hooks where returning an empty array after calling `fetchMore` would rerender the component with an empty list. + +- [#11694](https://github.com/apollographql/apollo-client/pull/11694) [`835d5f3`](https://github.com/apollographql/apollo-client/commit/835d5f30c532c432e2434561580e6f1ec44cc908) Thanks [@phryneas](https://github.com/phryneas)! - Expose `setErrorMessageHandler` from `@apollo/client/dev` entrypoint. + +- [#11689](https://github.com/apollographql/apollo-client/pull/11689) [`cb8ffe5`](https://github.com/apollographql/apollo-client/commit/cb8ffe50e903397f741b62a44624bfe69b5f7b75) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where passing a new `from` option to `useFragment` would first render with the previous value before rerendering with the correct value. + +- [#11713](https://github.com/apollographql/apollo-client/pull/11713) [`642092c`](https://github.com/apollographql/apollo-client/commit/642092c713199093aede45f105a1ee3f637614cd) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where setting a default `watchQuery` option in the `ApolloClient` constructor could break `startTransition` when used with suspense hooks. + +## 3.9.7 + +### Patch Changes + +- [#11659](https://github.com/apollographql/apollo-client/pull/11659) [`652a61e`](https://github.com/apollographql/apollo-client/commit/652a61e96db0f0e27d0a22fafae1df388f3fdf36) Thanks [@phryneas](https://github.com/phryneas)! - Make `useRenderGuard` more resilient to changes in React internals. + +- [#11594](https://github.com/apollographql/apollo-client/pull/11594) [`50b1097`](https://github.com/apollographql/apollo-client/commit/50b10970ca0efa290ae415ef801650327a89ab8e) Thanks [@alessbell](https://github.com/alessbell)! - Adds a fix for multipart subscriptions that terminate with payload: null + +## 3.9.6 + +### Patch Changes + +- [#11617](https://github.com/apollographql/apollo-client/pull/11617) [`f1d8bc4`](https://github.com/apollographql/apollo-client/commit/f1d8bc40c3d8e39340f721f4f1c3fd0ed77b8a6b) Thanks [@phryneas](https://github.com/phryneas)! - Allow Apollo Client instance to intercept hook functionality + +- [#11638](https://github.com/apollographql/apollo-client/pull/11638) [`bf93ada`](https://github.com/apollographql/apollo-client/commit/bf93adaa0321b573db0ea8fc3a5c364e1fdfeef3) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where calling `fetchMore` from a suspense-enabled hook inside `startTransition` caused an unnecessary rerender. + +## 3.9.5 + +### Patch Changes + +- [#11595](https://github.com/apollographql/apollo-client/pull/11595) [`8c20955`](https://github.com/apollographql/apollo-client/commit/8c20955874562e5b2ab35557325e047b059bc4fc) Thanks [@phryneas](https://github.com/phryneas)! - Bumps the dependency `rehackt` to 0.0.5 + +- [#11592](https://github.com/apollographql/apollo-client/pull/11592) [`1133469`](https://github.com/apollographql/apollo-client/commit/1133469bd91ff76b9815e815a454a79d8e23a9bc) Thanks [@Stephen2](https://github.com/Stephen2)! - Strengthen `MockedResponse.newData` type + +- [#11579](https://github.com/apollographql/apollo-client/pull/11579) [`1ba2fd9`](https://github.com/apollographql/apollo-client/commit/1ba2fd919f79dfdc7b9d3f7d1a7aa5918e648349) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue where partial data is reported to `useQuery` when using `notifyOnNetworkStatusChange` after it errors while another overlapping query succeeds. + +- [#11579](https://github.com/apollographql/apollo-client/pull/11579) [`1ba2fd9`](https://github.com/apollographql/apollo-client/commit/1ba2fd919f79dfdc7b9d3f7d1a7aa5918e648349) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where a partial cache write for an errored query would result in automatically refetching that query. + +- [#11562](https://github.com/apollographql/apollo-client/pull/11562) [`65ab695`](https://github.com/apollographql/apollo-client/commit/65ab695470741e8dcaef1ebd7742c3c397526354) Thanks [@mspiess](https://github.com/mspiess)! - Mocks with an infinite delay no longer require result or error + +## 3.9.4 + +### Patch Changes + +- [#11403](https://github.com/apollographql/apollo-client/pull/11403) [`b0c4f3a`](https://github.com/apollographql/apollo-client/commit/b0c4f3ad8198981a229b46dc430345a76e577e9c) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix issue in `useLazyQuery` that results in a double network call when calling the execute function with no arguments after having called it previously with another set of arguments. + +- [#11576](https://github.com/apollographql/apollo-client/pull/11576) [`e855d00`](https://github.com/apollographql/apollo-client/commit/e855d00447e4d9ae478d98f6796d842ef6cc76d1) Thanks [@alessbell](https://github.com/alessbell)! - Revert PR [#11202](https://github.com/apollographql/apollo-client/pull/11202) to fix caching bug reported in [#11560](https://github.com/apollographql/apollo-client/issues/11560) + +## 3.9.3 + +### Patch Changes + +- [#11525](https://github.com/apollographql/apollo-client/pull/11525) [`dce923a`](https://github.com/apollographql/apollo-client/commit/dce923ae57eb6b6d889e2980635cb90e2c6cbca3) Thanks [@vezaynk](https://github.com/vezaynk)! - Allows passing in client via options to useFragment + +- [#11558](https://github.com/apollographql/apollo-client/pull/11558) [`8cba16f`](https://github.com/apollographql/apollo-client/commit/8cba16f041609443111ecf5fb58faea1b3e79569) Thanks [@alessbell](https://github.com/alessbell)! - Fix [`unbound-method`](https://github.com/apollographql/apollo-client/issues/11554) linter error on ObservableQuery methods exposed on useQuery's QueryResult object. + +## 3.9.2 + +### Patch Changes + +- [#11552](https://github.com/apollographql/apollo-client/pull/11552) [`6ac2b0c`](https://github.com/apollographql/apollo-client/commit/6ac2b0ce4d999c63478d85b40ad56ccda9624797) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix import in `useLazyRef` causing import issues in the nextjs package. + +## 3.9.1 + +### Patch Changes + +- [#11516](https://github.com/apollographql/apollo-client/pull/11516) [`8390fea`](https://github.com/apollographql/apollo-client/commit/8390fea13175bada8361ba5f0df2e43197085aba) Thanks [@phryneas](https://github.com/phryneas)! - Fix an incorrect string substitution in a warning message. + +- [#11515](https://github.com/apollographql/apollo-client/pull/11515) [`c9bf93b`](https://github.com/apollographql/apollo-client/commit/c9bf93bdc2816f7fdba96961e1435f463f440bd1) Thanks [@vladar](https://github.com/vladar)! - Avoid redundant refetchQueries call for mutation with no-cache policy (fixes #10238) + +- [#11545](https://github.com/apollographql/apollo-client/pull/11545) [`84a6bea`](https://github.com/apollographql/apollo-client/commit/84a6beaeae69acdffea49ba6b8242752cc188172) Thanks [@alessbell](https://github.com/alessbell)! - Remove error thrown by `inFlightLinkObservables` intended to be removed before 3.9 release. + +## 3.9.0 + +### Minor Changes + +#### Memory optimizations + +- [#11424](https://github.com/apollographql/apollo-client/pull/11424) [`62f3b6d`](https://github.com/apollographql/apollo-client/commit/62f3b6d0e89611e27d9f29812ee60e5db5963fd6) Thanks [@phryneas](https://github.com/phryneas)! - Simplify RetryLink, fix potential memory leak + + Historically, `RetryLink` would keep a `values` array of all previous values, in case the operation would get an additional subscriber at a later point in time. + + In practice, this could lead to a memory leak ([#11393](https://github.com/apollographql/apollo-client/pull/11393)) and did not serve any further purpose, as the resulting observable would only be subscribed to by Apollo Client itself, and only once - it would be wrapped in a `Concast` before being exposed to the user, and that `Concast` would handle subscribers on its own. + +- [#11435](https://github.com/apollographql/apollo-client/pull/11435) [`5cce53e`](https://github.com/apollographql/apollo-client/commit/5cce53e83b976f85d2d2b06e28cc38f01324fea1) Thanks [@phryneas](https://github.com/phryneas)! - Deprecates `canonizeResults`. + + Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + +- [#11254](https://github.com/apollographql/apollo-client/pull/11254) [`d08970d`](https://github.com/apollographql/apollo-client/commit/d08970d348cf4ad6d80c6baf85b4a4cd4034a3bb) Thanks [@benjamn](https://github.com/benjamn)! - Decouple `canonicalStringify` from `ObjectCanon` for better time and memory performance. + +- [#11356](https://github.com/apollographql/apollo-client/pull/11356) [`cc4ac7e`](https://github.com/apollographql/apollo-client/commit/cc4ac7e1917f046bcd177882727864eed40b910e) Thanks [@phryneas](https://github.com/phryneas)! - Fix a potential memory leak in `FragmentRegistry.transform` and `FragmentRegistry.findFragmentSpreads` that would hold on to passed-in `DocumentNodes` for too long. + +- [#11370](https://github.com/apollographql/apollo-client/pull/11370) [`25e2cb4`](https://github.com/apollographql/apollo-client/commit/25e2cb431c76ec5aa88202eaacbd98fad42edc7f) Thanks [@phryneas](https://github.com/phryneas)! - `parse` function: improve memory management + + - use LRU `WeakCache` instead of `Map` to keep a limited number of parsed results + - cache is initiated lazily, only when needed + - expose `parse.resetCache()` method + +- [#11389](https://github.com/apollographql/apollo-client/pull/11389) [`139acd1`](https://github.com/apollographql/apollo-client/commit/139acd1153afa1445b69dcb4e139668ab8c5889a) Thanks [@phryneas](https://github.com/phryneas)! - `documentTransform`: use `optimism` and `WeakCache` instead of directly storing data on the `Trie` + +- [#11358](https://github.com/apollographql/apollo-client/pull/11358) [`7d939f8`](https://github.com/apollographql/apollo-client/commit/7d939f80fbc2c419c58a6c55b6a35ee7474d0379) Thanks [@phryneas](https://github.com/phryneas)! - Fixes a potential memory leak in `Concast` that might have been triggered when `Concast` was used outside of Apollo Client. + +- [#11344](https://github.com/apollographql/apollo-client/pull/11344) [`bd26676`](https://github.com/apollographql/apollo-client/commit/bd2667619700139af32a45364794d11f845ab6cf) Thanks [@phryneas](https://github.com/phryneas)! - Add a `resetCache` method to `DocumentTransform` and hook `InMemoryCache.addTypenameTransform` up to `InMemoryCache.gc` + +- [#11367](https://github.com/apollographql/apollo-client/pull/11367) [`30d17bf`](https://github.com/apollographql/apollo-client/commit/30d17bfebe44dbfa7b78c8982cfeb49afd37129c) Thanks [@phryneas](https://github.com/phryneas)! - `print`: use `WeakCache` instead of `WeakMap` + +- [#11387](https://github.com/apollographql/apollo-client/pull/11387) [`4dce867`](https://github.com/apollographql/apollo-client/commit/4dce8673b1757d8a3a4edd2996d780e86fad14e3) Thanks [@phryneas](https://github.com/phryneas)! - `QueryManager.transformCache`: use `WeakCache` instead of `WeakMap` + +- [#11369](https://github.com/apollographql/apollo-client/pull/11369) [`2a47164`](https://github.com/apollographql/apollo-client/commit/2a471646616e3af1b5c039e961f8d5717fad8f32) Thanks [@phryneas](https://github.com/phryneas)! - Persisted Query Link: improve memory management + + - use LRU `WeakCache` instead of `WeakMap` to keep a limited number of hash results + - hash cache is initiated lazily, only when needed + - expose `persistedLink.resetHashCache()` method + - reset hash cache if the upstream server reports it doesn't accept persisted queries + +- [#10804](https://github.com/apollographql/apollo-client/pull/10804) [`221dd99`](https://github.com/apollographql/apollo-client/commit/221dd99ffd1990f8bd0392543af35e9b08d0fed8) Thanks [@phryneas](https://github.com/phryneas)! - use WeakMap in React Native with Hermes + +- [#11355](https://github.com/apollographql/apollo-client/pull/11355) [`7d8e184`](https://github.com/apollographql/apollo-client/commit/7d8e18493cd13134726c6643cbf0fadb08be2d37) Thanks [@phryneas](https://github.com/phryneas)! - InMemoryCache.gc now also triggers FragmentRegistry.resetCaches (if there is a FragmentRegistry) + +- [#11409](https://github.com/apollographql/apollo-client/pull/11409) [`2e7203b`](https://github.com/apollographql/apollo-client/commit/2e7203b3a9618952ddb522627ded7cceabd7f250) Thanks [@phryneas](https://github.com/phryneas)! - Adds an experimental `ApolloClient.getMemoryInternals` helper + +- [#11343](https://github.com/apollographql/apollo-client/pull/11343) [`776631d`](https://github.com/apollographql/apollo-client/commit/776631de4500d56252f6f5fdaf29a81c41dfbdc7) Thanks [@phryneas](https://github.com/phryneas)! - Add `reset` method to `print`, hook up to `InMemoryCache.gc` + +#### Suspense-enabled data fetching on user interaction with `useLoadableQuery` + +- [#11300](https://github.com/apollographql/apollo-client/pull/11300) [`a815873`](https://github.com/apollographql/apollo-client/commit/a8158733cfa3e65180ec23518d657ea41894bb2b) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Introduces a new `useLoadableQuery` hook. This hook works similarly to `useBackgroundQuery` in that it returns a `queryRef` that can be used to suspend a component via the `useReadQuery` hook. It provides a more ergonomic way to load the query during a user interaction (for example when wanting to preload some data) that would otherwise be clunky with `useBackgroundQuery`. + + ```tsx + function App() { + const [loadQuery, queryRef, { refetch, fetchMore, reset }] = + useLoadableQuery(query, options); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + function Child({ queryRef }) { + const { data } = useReadQuery(queryRef); + + // ... + } + ``` + +#### Begin preloading outside of React with `createQueryPreloader` + +- [#11412](https://github.com/apollographql/apollo-client/pull/11412) [`58db5c3`](https://github.com/apollographql/apollo-client/commit/58db5c3295b88162f91019f0898f6baa4b9cced6) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add the ability to start preloading a query outside React to begin fetching as early as possible. Call `createQueryPreloader` to create a `preloadQuery` function which can be called to start fetching a query. This returns a `queryRef` which is passed to `useReadQuery` and suspended until the query is done fetching. + + ```tsx + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(QUERY, { variables, ...otherOptions }); + + function App() { + return { + Loading}> + + + } + } + + function MyQuery() { + const { data } = useReadQuery(queryRef); + + // do something with data + } + ``` + +#### Testing utility improvements + +- [#11178](https://github.com/apollographql/apollo-client/pull/11178) [`4d64a6f`](https://github.com/apollographql/apollo-client/commit/4d64a6fa2ad5abe6f7f172c164f5e1fc2cb89829) Thanks [@sebakerckhof](https://github.com/sebakerckhof)! - Support re-using of mocks in the MockedProvider + +- [#6701](https://github.com/apollographql/apollo-client/pull/6701) [`8d2b4e1`](https://github.com/apollographql/apollo-client/commit/8d2b4e107d7c21563894ced3a65d631183b58fd9) Thanks [@prowe](https://github.com/prowe)! - Ability to dynamically match mocks + + Adds support for a new property `MockedResponse.variableMatcher`: a predicate function that accepts a `variables` param. If `true`, the `variables` will be passed into the `ResultFunction` to help dynamically build a response. + +#### New `useQueryRefHandlers` hook + +- [#11412](https://github.com/apollographql/apollo-client/pull/11412) [`58db5c3`](https://github.com/apollographql/apollo-client/commit/58db5c3295b88162f91019f0898f6baa4b9cced6) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Create a new `useQueryRefHandlers` hook that returns `refetch` and `fetchMore` functions for a given `queryRef`. This is useful to get access to handlers for a `queryRef` that was created by `createQueryPreloader` or when the handlers for a `queryRef` produced by a different component are inaccessible. + + ```jsx + const MyComponent({ queryRef }) { + const { refetch, fetchMore } = useQueryRefHandlers(queryRef); + + // ... + } + ``` + +#### Bail out of `optimisticResponse` updates with the `IGNORE` sentinel object + +- [#11410](https://github.com/apollographql/apollo-client/pull/11410) [`07fcf6a`](https://github.com/apollographql/apollo-client/commit/07fcf6a3bf5bc78ffe6f3e598897246b4da02cbb) Thanks [@sf-twingate](https://github.com/sf-twingate)! - Allow returning `IGNORE` sentinel object from `optimisticResponse` functions to bail-out from the optimistic update. + + Consider this example: + + ```jsx + const UPDATE_COMMENT = gql` + mutation UpdateComment($commentId: ID!, $commentContent: String!) { + updateComment(commentId: $commentId, content: $commentContent) { + id + __typename + content + } + } + `; + + function CommentPageWithData() { + const [mutate] = useMutation(UPDATE_COMMENT); + return ( + + mutate({ + variables: { commentId, commentContent }, + optimisticResponse: (vars, { IGNORE }) => { + if (commentContent === "foo") { + // conditionally bail out of optimistic updates + return IGNORE; + } + return { + updateComment: { + id: commentId, + __typename: "Comment", + content: commentContent, + }, + }; + }, + }) + } + /> + ); + } + ``` + + The `IGNORE` sentinel can be destructured from the second parameter in the callback function signature passed to `optimisticResponse`. + +#### Network adapters for multipart subscriptions usage with Relay and urql + +- [#11301](https://github.com/apollographql/apollo-client/pull/11301) [`46ab032`](https://github.com/apollographql/apollo-client/commit/46ab032af83a01f184bfcce5edba4b55dbb2962a) Thanks [@alessbell](https://github.com/alessbell)! - Add multipart subscription network adapters for Relay and urql + + ##### Relay + + ```tsx + import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/relay"; + import { Environment, Network, RecordSource, Store } from "relay-runtime"; + + const fetchMultipartSubs = createFetchMultipartSubscription( + "http://localhost:4000", + ); + + const network = Network.create(fetchQuery, fetchMultipartSubs); + + export const RelayEnvironment = new Environment({ + network, + store: new Store(new RecordSource()), + }); + ``` + + ##### Urql + + ```tsx + import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/urql"; + import { Client, fetchExchange, subscriptionExchange } from "@urql/core"; + + const url = "http://localhost:4000"; + + const multipartSubscriptionForwarder = createFetchMultipartSubscription(url); + + const client = new Client({ + url, + exchanges: [ + fetchExchange, + subscriptionExchange({ + forwardSubscription: multipartSubscriptionForwarder, + }), + ], + }); + ``` + +#### `skipPollAttempt` callback function + +- [#11397](https://github.com/apollographql/apollo-client/pull/11397) [`3f7eecb`](https://github.com/apollographql/apollo-client/commit/3f7eecbfbd4f4444cffcaac7dd9fd225c8c2a401) Thanks [@aditya-kumawat](https://github.com/aditya-kumawat)! - Adds a new `skipPollAttempt` callback function that's called whenever a refetch attempt occurs while polling. If the function returns `true`, the refetch is skipped and not reattempted until the next poll interval. This will solve the frequent use-case of disabling polling when the window is inactive. + + ```ts + useQuery(QUERY, { + pollInterval: 1000, + skipPollAttempt: () => document.hidden, // or !document.hasFocus() + }); + // or define it globally + new ApolloClient({ + defaultOptions: { + watchQuery: { + skipPollAttempt: () => document.hidden, // or !document.hasFocus() + }, + }, + }); + ``` + +#### `QueryManager.inFlightLinkObservables` now uses a strong `Trie` as an internal data structure + +- [#11345](https://github.com/apollographql/apollo-client/pull/11345) [`1759066`](https://github.com/apollographql/apollo-client/commit/1759066a8f9a204e49228568aef9446a64890ff3) Thanks [@phryneas](https://github.com/phryneas)! + + ##### Warning: requires `@apollo/experimental-nextjs-app-support` update + + If you are using `@apollo/experimental-nextjs-app-support`, you will need to update that to at least 0.5.2, as it accesses this internal data structure. + +
+

More Minor Changes

+ +- [#11202](https://github.com/apollographql/apollo-client/pull/11202) [`7c2bc08`](https://github.com/apollographql/apollo-client/commit/7c2bc08b2ab46b9aa181d187a27aec2ad7129599) Thanks [@benjamn](https://github.com/benjamn)! - Prevent `QueryInfo#markResult` mutation of `result.data` and return cache data consistently whether complete or incomplete. + +- [#11442](https://github.com/apollographql/apollo-client/pull/11442) [`4b6f2bc`](https://github.com/apollographql/apollo-client/commit/4b6f2bccf3ba94643b38689b32edd2839e47aec1) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove the need to call `retain` from `useLoadableQuery` since `useReadQuery` will now retain the query. This means that a `queryRef` that is not consumed by `useReadQuery` within the given `autoDisposeTimeoutMs` will now be auto diposed for you. + + Thanks to [#11412](https://github.com/apollographql/apollo-client/pull/11412), disposed query refs will be automatically resubscribed to the query when consumed by `useReadQuery` after it has been disposed. + +- [#11438](https://github.com/apollographql/apollo-client/pull/11438) [`6d46ab9`](https://github.com/apollographql/apollo-client/commit/6d46ab930a5e9bd5cae153d3b75b8966784fcd4e) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Remove the need to call `retain` from `useBackgroundQuery` since `useReadQuery` will now retain the query. This means that a `queryRef` that is not consumed by `useReadQuery` within the given `autoDisposeTimeoutMs` will now be auto diposed for you. + + Thanks to [#11412](https://github.com/apollographql/apollo-client/pull/11412), disposed query refs will be automatically resubscribed to the query when consumed by `useReadQuery` after it has been disposed. + +- [#11175](https://github.com/apollographql/apollo-client/pull/11175) [`d6d1491`](https://github.com/apollographql/apollo-client/commit/d6d14911c40782cd6d69167b6f6169c890091ccb) Thanks [@phryneas](https://github.com/phryneas)! - To work around issues in React Server Components, especially with bundling for + the Next.js "edge" runtime we now use an external package to wrap `react` imports + instead of importing React directly. + +- [#11495](https://github.com/apollographql/apollo-client/pull/11495) [`1190aa5`](https://github.com/apollographql/apollo-client/commit/1190aa59a106217f7192c1f81099adfa5e4365c1) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Increase the default memory limits for `executeSelectionSet` and `executeSelectionSetArray`. + +
+ +
+

Patch Changes

+ +- [#11275](https://github.com/apollographql/apollo-client/pull/11275) [`3862f9b`](https://github.com/apollographql/apollo-client/commit/3862f9ba9086394c4cf4c2ecd99e8e0f6cf44885) Thanks [@phryneas](https://github.com/phryneas)! - 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; + } + ``` + +- [#11443](https://github.com/apollographql/apollo-client/pull/11443) [`ff5a332`](https://github.com/apollographql/apollo-client/commit/ff5a332ff8b190c418df25371e36719d70061ebe) Thanks [@phryneas](https://github.com/phryneas)! - Adds a deprecation warning to the HOC and render prop APIs. + + The HOC and render prop APIs have already been deprecated since 2020, + but we previously didn't have a `@deprecated` tag in the DocBlocks. + +- [#11385](https://github.com/apollographql/apollo-client/pull/11385) [`d9ca4f0`](https://github.com/apollographql/apollo-client/commit/d9ca4f0821c66ae4f03cf35a7ac93fe604cc6de3) Thanks [@phryneas](https://github.com/phryneas)! - ensure `defaultContext` is also used for mutations and subscriptions + +- [#11503](https://github.com/apollographql/apollo-client/pull/11503) [`67f62e3`](https://github.com/apollographql/apollo-client/commit/67f62e359bc471787d066319326e5582b4a635c8) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Release changes from [`v3.8.10`](https://github.com/apollographql/apollo-client/releases/tag/v3.8.10) + +- [#11078](https://github.com/apollographql/apollo-client/pull/11078) [`14edebe`](https://github.com/apollographql/apollo-client/commit/14edebebefb7634c32b921d02c1c85c6c8737989) Thanks [@phryneas](https://github.com/phryneas)! - ObservableQuery: prevent reporting results of previous queries if the variables changed since + +- [#11439](https://github.com/apollographql/apollo-client/pull/11439) [`33454f0`](https://github.com/apollographql/apollo-client/commit/33454f0a40a05ea2b00633bda20a84d0ec3a4f4d) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Address bundling issue introduced in [#11412](https://github.com/apollographql/apollo-client/pull/11412) where the `react/cache` internals ended up duplicated in the bundle. This was due to the fact that we had a `react/hooks` entrypoint that imported these files along with the newly introduced `createQueryPreloader` function, which lived outside of the `react/hooks` folder. + +- [#11371](https://github.com/apollographql/apollo-client/pull/11371) [`ebd8fe2`](https://github.com/apollographql/apollo-client/commit/ebd8fe2c1b8b50bfeb2da20aeca5671300fb5564) Thanks [@phryneas](https://github.com/phryneas)! - Clarify types of `EntityStore.makeCacheKey`. + +
+ +## 3.8.10 + +### Patch Changes + +- [#11489](https://github.com/apollographql/apollo-client/pull/11489) [`abfd02a`](https://github.com/apollographql/apollo-client/commit/abfd02abeb8585e44377e9e87e5d20e5d95be002) Thanks [@gronxb](https://github.com/gronxb)! - Fix `networkStatus` with `useSuspenseQuery` not properly updating to ready state when using a `cache-and-network` fetch policy that returns data equal to what is already in the cache. + +- [#11483](https://github.com/apollographql/apollo-client/pull/11483) [`6394dda`](https://github.com/apollographql/apollo-client/commit/6394dda47fa83d9ddd922e0d05e62bd872e4ea8e) Thanks [@pipopotamasu](https://github.com/pipopotamasu)! - Fix cache override warning output + +## 3.8.9 + +### Patch Changes + +- [#11472](https://github.com/apollographql/apollo-client/pull/11472) [`afc844d`](https://github.com/apollographql/apollo-client/commit/afc844dd8d6f9f7a3e2003f9a5b541291dfe3fb4) Thanks [@alessbell](https://github.com/alessbell)! - Fix delay: Infinity when set on a MockResponse passed to Mocked Provider so it indefinitely enters loading state. + +- [#11464](https://github.com/apollographql/apollo-client/pull/11464) [`aac12b2`](https://github.com/apollographql/apollo-client/commit/aac12b221a6cb776d4941b6c8aadf04f0f0acd27) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Prevent `useFragment` from excessively unsubscribing and resubscribing the fragment with the cache on every render. + +- [#11449](https://github.com/apollographql/apollo-client/pull/11449) [`f40cda4`](https://github.com/apollographql/apollo-client/commit/f40cda45841e93b056c781c19651b54464f7346a) Thanks [@phryneas](https://github.com/phryneas)! - Removes refences to the typescript "dom" lib. + +- [#11470](https://github.com/apollographql/apollo-client/pull/11470) [`e293bc9`](https://github.com/apollographql/apollo-client/commit/e293bc90d6f7937a6fc7c169f7b16eeb39d5fd49) Thanks [@phryneas](https://github.com/phryneas)! - Remove an unnecessary check from parseAndCheckHttpResponse. + +## 3.8.8 + +### Patch Changes + +- [#11200](https://github.com/apollographql/apollo-client/pull/11200) [`ae5091a21`](https://github.com/apollographql/apollo-client/commit/ae5091a21f0feff1486503071ea8dc002cf1be41) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Enable `strict` in tsconfig for the entire project. + +- [#11332](https://github.com/apollographql/apollo-client/pull/11332) [`291aea56b`](https://github.com/apollographql/apollo-client/commit/291aea56bfaed3987a98be7fe4e6160114b62d2d) Thanks [@asvishnyakov](https://github.com/asvishnyakov)! - Add missed reexports of MutationFetchPolicy and RefetchWritePolicy to @apollo/client/core + +- [#10931](https://github.com/apollographql/apollo-client/pull/10931) [`e5acf910e`](https://github.com/apollographql/apollo-client/commit/e5acf910e39752b453540b6751046d1c19b66350) Thanks [@phryneas](https://github.com/phryneas)! - `useMutation`: also reset internal state on reset + +## 3.8.7 + +### Patch Changes + +- [#11297](https://github.com/apollographql/apollo-client/pull/11297) [`c8c76a522`](https://github.com/apollographql/apollo-client/commit/c8c76a522e593de0d06cff73fde2d9e88152bed6) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Add an explicit return type for the `useReadQuery` hook called `UseReadQueryResult`. Previously the return type of this hook was inferred from the return value. + +- [#11337](https://github.com/apollographql/apollo-client/pull/11337) [`bb1da8349`](https://github.com/apollographql/apollo-client/commit/bb1da8349e785c54fb4030f269602c900adf23a0) Thanks [@phryneas](https://github.com/phryneas)! - #11206 used the TypeScript syntax `infer X extends Y` that was introduced in TS 4.8. + This caused some problems for some users, so we are rolling back to a more backwars-compatible (albeit slightly less performant) type. + ## 3.8.6 ### Patch Changes @@ -180,7 +761,7 @@ return data.breeds.map(({ characteristics }) => characteristics.map((characteristic) => (
{characteristic}
- )) + )), ); } ``` @@ -231,7 +812,7 @@ const { data } = useSuspenseQuery( query, - id ? { variables: { id } } : skipToken + id ? { variables: { id } } : skipToken, ); ``` @@ -2186,7 +2767,7 @@ In upcoming v3.6.x and v3.7 (beta) releases, we will be completely overhauling o fields: { comments(comments: Reference[], { readField }) { return comments.filter( - (comment) => idToRemove !== readField("id", comment) + (comment) => idToRemove !== readField("id", comment), ); }, }, diff --git a/README.md b/README.md index ffeecf275fb..8340c856e8d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Apollo Client is a fully-featured caching GraphQL client with integrations for React, Angular, and more. It allows you to easily build UI components that fetch data via GraphQL. +| ☑️ Apollo Client User Survey | +| :----- | +| What do you like best about Apollo Client? What needs to be improved? Please tell us by taking a [one-minute survey](https://docs.google.com/forms/d/e/1FAIpQLSczNDXfJne3ZUOXjk9Ursm9JYvhTh1_nFTDfdq3XBAFWCzplQ/viewform?usp=pp_url&entry.1170701325=Apollo+Client&entry.204965213=Readme). Your responses will help us understand Apollo Client usage and allow us to serve you better. | + ## Documentation All Apollo Client documentation, including React integration articles and helpful recipes, can be found at:
diff --git a/ROADMAP.md b/ROADMAP.md index 081d877de06..575e97eb231 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # 🔮 Apollo Client Roadmap -**Last updated: 2023-09-26** +**Last updated: 2024-06-03** For up to date release notes, refer to the project's [Changelog](https://github.com/apollographql/apollo-client/blob/main/CHANGELOG.md). @@ -13,25 +13,17 @@ For up to date release notes, refer to the project's [Changelog](https://github. --- -## [3.9.0](https://github.com/apollographql/apollo-client/milestone/32) +## [3.11.0](https://github.com/apollographql/apollo-client/milestone/40) - July 9th, 2024 +_Release candidate - July 2nd, 2024_ -_Currently in planning phase_ +- Rewriting `useQuery` and `useSubscription` for better React Compiler support -Features we plan to tackle: +## Upcoming features +- Data masking - Introduce a suspenseful `useFragment` that will suspend when the data is not yet loaded -- Ability to preload a query outside of a React component that can be used with `useReadQuery` to suspend while loading -- Introduce a new `useInteractiveQuery`/`useLazyBackgroundQuery` hook (name TBD) -- Improved testing utilities -- Optimizing memory usage in SSR scenarios - -> NOTE: These are subject to change and are not guaranteed to be part of 3.9 at the time of this writing. - -## Future 3.x releases - -_Approximate Date: TBD_ - -The 3.8 release was a major milestone for the project's React support. Feedback from the community will have a big impact on where we go next, particularly as use cases for React Server Components and other React 18 features emerge. In addition to new functionality, there is a significant backlog of questions and fixes that we want to categorize and thoughtfully address in upcoming releases. +- Leaner client (under alternate entry point) +- Better types for `useQuery`/`useMutation`/`useSubscription` ## 4.0 diff --git a/api-extractor.json b/api-extractor.json index 20f6605a52a..d026e304ed8 100644 --- a/api-extractor.json +++ b/api-extractor.json @@ -7,6 +7,7 @@ "mainEntryPointFilePath": "/dist/index.d.ts", "bundledPackages": [], "newlineKind": "lf", + "enumMemberOrder": "preserve", /** * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the @@ -37,14 +38,17 @@ }, "docModel": { - "enabled": false + "enabled": false, + "apiJsonFilePath": "/docs/public/.api.json", + "includeForgottenExports": true }, "dtsRollup": { "enabled": false }, "tsdocMetadata": { - "enabled": false + "enabled": false, + "tsdocMetadataFilePath": "/docs/public/tsdoc-metadata.json" }, /** @@ -113,14 +117,23 @@ "ae-missing-release-tag": { "logLevel": "none" + }, + + "ae-internal-missing-underscore": { + "logLevel": "none", + "addToApiReportFile": false + }, + + "ae-unresolved-link": { + "logLevel": "warning", + "addToApiReportFile": true + } + }, + "tsdocMessageReporting": { + "tsdoc-escape-greater-than": { + "logLevel": "none", + "addToApiReportFile": false } } - - // "ae-extra-release-tag": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . } } diff --git a/config/FixJSDOMEnvironment.js b/config/FixJSDOMEnvironment.js new file mode 100644 index 00000000000..dbbf9d3356f --- /dev/null +++ b/config/FixJSDOMEnvironment.js @@ -0,0 +1,21 @@ +const { default: JSDOMEnvironment } = require("jest-environment-jsdom"); + +// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string +class FixJSDOMEnvironment extends JSDOMEnvironment { + constructor(...args) { + super(...args); + + // FIXME https://github.com/jsdom/jsdom/issues/1724 + this.global.Headers = Headers; + this.global.Request = Request; + this.global.Response = Response; + + // FIXME: setting a global fetch breaks HttpLink tests + // and setting AbortController breaks PersistedQueryLink tests, which may + // indicate a memory leak + // this.global.fetch = fetch; + this.global.AbortController = AbortController; + } +} + +module.exports = FixJSDOMEnvironment; diff --git a/config/apiExtractor.ts b/config/apiExtractor.ts index c8d6dd86ec0..b902353a14c 100644 --- a/config/apiExtractor.ts +++ b/config/apiExtractor.ts @@ -2,10 +2,37 @@ import * as path from "path"; import { Extractor, ExtractorConfig, - ExtractorResult, + ExtractorLogLevel, + IConfigFile, } from "@microsoft/api-extractor"; +import { parseArgs } from "node:util"; +import fs from "node:fs"; + // @ts-ignore -import { map } from "./entryPoints.js"; +import { map, buildDocEntryPoints } from "./entryPoints.js"; +import { readFileSync } from "fs"; + +const parsed = parseArgs({ + options: { + generate: { + type: "string", + multiple: true, + default: ["apiReport"], + }, + "main-only": { + type: "boolean", + default: false, + }, + }, +}); + +if ( + !parsed.values.generate!.every((v) => ["apiReport", "docModel"].includes(v)) +) { + throw new Error( + "invalid value for --generate. Only allowed values are `apiReport` and `docModel`!" + ); +} // Load and parse the api-extractor.json file const configObjectFullPath = path.resolve(__dirname, "../api-extractor.json"); @@ -14,42 +41,106 @@ const packageJsonFullPath = path.resolve(__dirname, "../package.json"); process.exitCode = 0; -map((entryPoint: { dirs: string[] }) => { - const path = entryPoint.dirs.join("/"); - const mainEntryPointFilePath = - `/dist/${path}/index.d.ts`.replace("//", "/"); - console.log( - "\n\nCreating API extractor report for " + mainEntryPointFilePath - ); +const tempDir = fs.mkdtempSync("api-model"); +try { + if (parsed.values.generate?.includes("docModel")) { + console.log( + "\n\nCreating API extractor docmodel for the a combination of all entry points" + ); + const entryPointFile = path.join(tempDir, "entry.d.ts"); + fs.writeFileSync(entryPointFile, buildDocEntryPoints()); - const extractorConfig: ExtractorConfig = ExtractorConfig.prepare({ - configObject: { - ...baseConfig, - mainEntryPointFilePath, - apiReport: { - enabled: true, - ...baseConfig.apiReport, - reportFileName: `api-report${ - path ? "-" + path.replace("/", "_") : "" - }.md`, - }, - }, + buildReport(entryPointFile, "docModel"); + } + + if (parsed.values.generate?.includes("apiReport")) { + map((entryPoint: { dirs: string[] }) => { + const path = entryPoint.dirs.join("/"); + const mainEntryPointFilePath = + `/dist/${path}/index.d.ts`.replace("//", "/"); + console.log( + "\n\nCreating API extractor report for " + mainEntryPointFilePath + ); + buildReport( + mainEntryPointFilePath, + "apiReport", + `api-report${path ? "-" + path.replace(/\//g, "_") : ""}.md` + ); + }); + } +} finally { + fs.rmSync(tempDir, { recursive: true }); +} + +function buildReport( + mainEntryPointFilePath: string, + mode: "apiReport" | "docModel", + reportFileName = "" +) { + const configObject: IConfigFile = { + ...(JSON.parse(JSON.stringify(baseConfig)) as IConfigFile), + mainEntryPointFilePath, + }; + + if (mode === "apiReport") { + configObject.apiReport!.enabled = true; + configObject.docModel = { enabled: false }; + configObject.messages!.extractorMessageReporting![ + "ae-unresolved-link" + ]!.logLevel = ExtractorLogLevel.None; + configObject.apiReport!.reportFileName = reportFileName; + } else { + configObject.docModel!.enabled = true; + configObject.apiReport = { + enabled: false, + // this has to point to an existing folder, otherwise the extractor will fail + // but it will not write the file + reportFileName: "disabled.md", + reportFolder: tempDir, + }; + } + + const extractorConfig = ExtractorConfig.prepare({ + configObject, packageJsonFullPath, configObjectFullPath, }); - const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { - localBuild: process.env.CI === undefined, + const extractorResult = Extractor.invoke(extractorConfig, { + localBuild: process.env.CI === undefined || process.env.CI === "false", showVerboseMessages: true, }); - if (extractorResult.succeeded) { + let succeededAdditionalChecks = true; + if (fs.existsSync(extractorConfig.reportFilePath)) { + const contents = readFileSync(extractorConfig.reportFilePath, "utf8"); + if (contents.includes("rehackt")) { + succeededAdditionalChecks = false; + console.error( + "❗ %s contains a reference to the `rehackt` package!", + extractorConfig.reportFilePath + ); + } + if (contents.includes('/// ')) { + succeededAdditionalChecks = false; + console.error( + "❗ %s contains a reference to the global `React` type!/n" + + 'Use `import type * as ReactTypes from "react";` instead', + extractorConfig.reportFilePath + ); + } + } + + if (extractorResult.succeeded && succeededAdditionalChecks) { console.log(`✅ API Extractor completed successfully`); } else { console.error( `❗ API Extractor completed with ${extractorResult.errorCount} errors` + ` and ${extractorResult.warningCount} warnings` ); + if (!succeededAdditionalChecks) { + console.error("Additional checks failed."); + } process.exitCode = 1; } -}); +} diff --git a/config/entryPoints.js b/config/entryPoints.js index dbd41ad4d64..674e1cc9ba2 100644 --- a/config/entryPoints.js +++ b/config/entryPoints.js @@ -22,11 +22,15 @@ const entryPoints = [ { dirs: ["react", "context"] }, { dirs: ["react", "hoc"] }, { dirs: ["react", "hooks"] }, + { dirs: ["react", "internal"] }, { dirs: ["react", "parser"] }, { dirs: ["react", "ssr"] }, { dirs: ["testing"], extensions: [".js", ".jsx"] }, { dirs: ["testing", "core"] }, + { dirs: ["testing", "experimental"] }, { dirs: ["utilities"] }, + { dirs: ["utilities", "subscriptions", "relay"] }, + { dirs: ["utilities", "subscriptions", "urql"] }, { dirs: ["utilities", "globals"], sideEffects: true }, ]; @@ -123,3 +127,14 @@ function arraysEqualUpTo(a, b, end) { } return true; } + +exports.buildDocEntryPoints = () => { + const dist = path.resolve(__dirname, "../dist"); + const entryPoints = exports.map((entryPoint) => { + return `export * from "${dist}/${entryPoint.dirs.join("/")}/index.d.ts";`; + }); + entryPoints.push( + `export * from "${dist}/react/types/types.documentation.ts";` + ); + return entryPoints.join("\n"); +}; diff --git a/config/inlineInheritDoc.ts b/config/inlineInheritDoc.ts new file mode 100644 index 00000000000..558be9f375f --- /dev/null +++ b/config/inlineInheritDoc.ts @@ -0,0 +1,173 @@ +/** + * This CLI tool will inline docblocks specified with `@inheritDoc` on build. + * + * + * E.g. a in a dockblock like this: + * ```js + * /** {@inheritDoc @apollo/client!QueryOptions#query:member} *\/ + * ``` + * + * the annotation (everything from `{` to `}`) will be replaced with the docblock + * of the `QueryOptions.query` member function. + * + * We need this here for situations where inheritance is not possible for `docModel` + * generation (`interface Foo extends Omit {}` is too complicated a + * type for it to parse) and we want to flatten types - or going forward, for + * generally flattening types without repeating docs everywhere. + * + * You can get these "canonical ids" by running + * ```sh + * yarn docmodel + * ``` + * and looking at the generated [`client.api.json`](../docs/shared/client.api.json) file. + */ +/** End file docs */ + +// @ts-ignore +import { buildDocEntryPoints } from "./entryPoints.js"; +// @ts-ignore +import { Project, ts, printNode, Node } from "ts-morph"; +import { ApiModel, ApiDocumentedItem } from "@microsoft/api-extractor-model"; +import { DeclarationReference } from "@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference"; +import { StringBuilder, TSDocEmitter } from "@microsoft/tsdoc"; + +import fs from "node:fs"; +import path from "node:path"; +import { + Extractor, + ExtractorConfig, + ExtractorLogLevel, +} from "@microsoft/api-extractor"; + +console.log( + "Processing {@inheritDoc } comments in .d.ts files." +); + +const model = loadApiModel(); +processComments(); + +function getCommentFor(canonicalReference: string) { + const apiItem = model.resolveDeclarationReference( + DeclarationReference.parse(canonicalReference), + undefined + ).resolvedApiItem; + if (!apiItem) + throw new Error( + `Could not resolve canonical reference "${canonicalReference}"` + ); + if (apiItem instanceof ApiDocumentedItem) { + if (!apiItem.tsdocComment) return ""; + const stringBuilder = new StringBuilder(); + const emitter = new TSDocEmitter(); + emitter["_emitCommentFraming"] = false; + emitter["_renderCompleteObject"](stringBuilder, apiItem.tsdocComment); + return stringBuilder.toString(); + } else { + throw new Error( + `"${canonicalReference}" is not documented, so no documentation can be inherited.` + ); + } +} + +function loadApiModel() { + const tempDir = fs.mkdtempSync("api-model"); + try { + const entryPointFile = path.join(tempDir, "entry.d.ts"); + fs.writeFileSync(entryPointFile, buildDocEntryPoints()); + + // Load and parse the api-extractor.json file + const configObjectFullPath = path.resolve( + __dirname, + "../api-extractor.json" + ); + const packageJsonFullPath = path.resolve(__dirname, "../package.json"); + const tempModelFile = path.join(tempDir, "client.api.json"); + + const configObject = ExtractorConfig.loadFile(configObjectFullPath); + configObject.mainEntryPointFilePath = entryPointFile; + configObject.docModel = { + ...configObject.docModel, + enabled: true, + apiJsonFilePath: tempModelFile, + }; + configObject.apiReport = { + enabled: false, + reportFileName: "disabled.md", + reportFolder: tempDir, + }; + + configObject.messages = { + extractorMessageReporting: { + default: { + logLevel: ExtractorLogLevel.None, + }, + }, + compilerMessageReporting: { + default: { + logLevel: ExtractorLogLevel.None, + }, + }, + tsdocMessageReporting: { + default: { + logLevel: ExtractorLogLevel.None, + }, + }, + }; + const extractorConfig = ExtractorConfig.prepare({ + configObject, + packageJsonFullPath, + configObjectFullPath, + }); + + Extractor.invoke(extractorConfig); + + const model = new ApiModel(); + model.loadPackage(tempModelFile); + return model; + } finally { + fs.rmSync(tempDir, { recursive: true }); + } +} + +function processComments() { + const inheritDocRegex = /\{@inheritDoc ([^}]+)\}/; + + const project = new Project({ + tsConfigFilePath: "tsconfig.json", + skipAddingFilesFromTsConfig: true, + }); + + const sourceFiles = project.addSourceFilesAtPaths("dist/**/*.d.ts"); + for (const file of sourceFiles) { + file.forEachDescendant((node) => { + if ( + Node.isPropertySignature(node) || + Node.isMethodSignature(node) || + Node.isMethodDeclaration(node) || + Node.isCallSignatureDeclaration(node) || + Node.isInterfaceDeclaration(node) + ) { + const docsNode = node.getJsDocs()[0]; + if (!docsNode) return; + const oldText = docsNode.getInnerText(); + let newText = oldText; + while (inheritDocRegex.test(newText)) { + newText = newText.replace( + inheritDocRegex, + (_, canonicalReference) => { + return getCommentFor(canonicalReference) || ""; + } + ); + } + if (oldText !== newText) { + docsNode.replaceWithText(frameComment(newText)) as any; + } + } + }); + file.saveSync(); + } +} + +function frameComment(text: string) { + return `/**\n * ${text.trim().replace(/\n/g, "\n * ")}\n */`; +} diff --git a/config/jest.config.js b/config/jest.config.js index 3dcd6e6de56..33e7aba59df 100644 --- a/config/jest.config.js +++ b/config/jest.config.js @@ -1,7 +1,7 @@ const defaults = { rootDir: "src", preset: "ts-jest", - testEnvironment: "jsdom", + testEnvironment: require.resolve("./FixJSDOMEnvironment.js"), setupFilesAfterEnv: ["/config/jest/setup.ts"], globals: { __DEV__: true, @@ -29,12 +29,26 @@ const defaults = { const ignoreTSFiles = ".ts$"; const ignoreTSXFiles = ".tsx$"; +const react19TestFileIgnoreList = [ + ignoreTSFiles, + // The HOCs and Render Prop Components have been deprecated since March 2020, + // and to test them we would need to rewrite a lot of our test suites. + // We will not support them any more for React 19. + // They will probably work, but we make no more guarantees. + "src/react/hoc/.*", + "src/react/components/.*", +]; + const react17TestFileIgnoreList = [ ignoreTSFiles, - // For now, we only support useSuspenseQuery with React 18, so no need to test - // it with React 17 + // We only support Suspense with React 18, so don't test suspense hooks with + // React 17 + "src/testing/experimental/__tests__/createTestSchema.test.tsx", "src/react/hooks/__tests__/useSuspenseQuery.test.tsx", "src/react/hooks/__tests__/useBackgroundQuery.test.tsx", + "src/react/hooks/__tests__/useLoadableQuery.test.tsx", + "src/react/hooks/__tests__/useQueryRefHandlers.test.tsx", + "src/react/query-preloader/__tests__/createQueryPreloader.test.tsx", ]; const tsStandardConfig = { @@ -45,6 +59,17 @@ const tsStandardConfig = { // For both React (Jest) "projects", ignore core tests (.ts files) as they // do not import React, to avoid running them twice. +const standardReact19Config = { + ...defaults, + displayName: "ReactDOM 19", + testPathIgnorePatterns: react19TestFileIgnoreList, + moduleNameMapper: { + "^react$": "react-19", + "^react-dom$": "react-dom-19", + "^react-dom/(.*)$": "react-dom-19/$1", + }, +}; + const standardReact18Config = { ...defaults, displayName: "ReactDOM 18", @@ -65,5 +90,10 @@ const standardReact17Config = { }; module.exports = { - projects: [tsStandardConfig, standardReact17Config, standardReact18Config], + projects: [ + tsStandardConfig, + standardReact17Config, + standardReact18Config, + standardReact19Config, + ], }; diff --git a/config/prepareChangesetsRelease.ts b/config/prepareChangesetsRelease.ts index d24a28a4c06..171503939b0 100644 --- a/config/prepareChangesetsRelease.ts +++ b/config/prepareChangesetsRelease.ts @@ -30,9 +30,9 @@ function copyDir(src: string, dest: string) { let srcPath = path.join(src, entry.name); let destPath = path.join(dest, entry.name); - entry.isDirectory() - ? copyDir(srcPath, destPath) - : fs.copyFileSync(srcPath, destPath); + entry.isDirectory() ? + copyDir(srcPath, destPath) + : fs.copyFileSync(srcPath, destPath); } } diff --git a/config/processInvariants.ts b/config/processInvariants.ts index 6534f36aa8a..13f8e275016 100644 --- a/config/processInvariants.ts +++ b/config/processInvariants.ts @@ -196,9 +196,10 @@ function transform(code: string, relativeFilePath: string) { }); if ( - !["utilities/globals/index.js", "config/jest/setup.js"].includes( - relativeFilePath - ) + ![ + osPathJoin("utilities", "globals", "index.js"), + osPathJoin("config", "jest", "setup.js"), + ].includes(relativeFilePath) ) recast.visit(ast, { visitIdentifier(path) { diff --git a/config/rollup.config.js b/config/rollup.config.js index 7917a340f56..5ed93b35165 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -3,10 +3,15 @@ import { promises as fs } from "fs"; import nodeResolve from "@rollup/plugin-node-resolve"; import { terser as minify } from "rollup-plugin-terser"; +import cleanup from "rollup-plugin-cleanup"; const entryPoints = require("./entryPoints"); const distDir = "./dist"; +const removeComments = cleanup({ + comments: ["some", /#__PURE__/, /#__NO_SIDE_EFFECTS__/], +}); + function isExternal(id, parentId, entryPointsAreExternal = true) { let posixId = toPosixPath(id); const posixParentId = toPosixPath(parentId); @@ -63,7 +68,7 @@ function prepareCJS(input, output) { exports: "named", externalLiveBindings: false, }, - plugins: [nodeResolve()], + plugins: [nodeResolve(), removeComments], }; } @@ -79,6 +84,9 @@ function prepareCJSMinified(input) { mangle: { toplevel: true, }, + format: { + comments: "some", // keeps comments with a @license, @copyright or @preserve tag + }, compress: { toplevel: true, global_defs: { @@ -111,6 +119,7 @@ function prepareBundle({ externalLiveBindings: false, }, plugins: [ + removeComments, { name: "externalize-dependency", resolveId(id, parentId) { diff --git a/docs/README.md b/docs/README.md index cb9d3ba95d3..9410556e966 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,4 +6,13 @@ The **deployed** version of the documentation for this repository is available a * https://www.apollographql.com/docs/react/ -See the [docs site README](https://github.com/apollographql/docs) for local installation and development. +For general local installation and development instructions, see the [docs site README](https://github.com/apollographql/docs). + +In order to build and develop the Apollo Client docs locally, you will need to follow these additional steps: + +1. Clone the docs site repository ([https://github.com/apollographql/docs](https://github.com/apollographql/docs)) in a sibling directory to your local copy of the [`apollo-client`](https://github.com/apollographql/apollo-client) repository. +2. `cd docs && npm i` +3. Open a new terminal, `cd apollo-client`, make changes to the docs and run `npm run docmodel` +4. Back in the terminal window where you've checked out and cd'd into the `docs` repository, run `DOCS_MODE='local' npm run start:local -- ../apollo-client` +5. Open a browser and visit `http://localhost:3000` +6. Note: you'll need to manually remove the `/react` segment of the URL from the path inside the browser diff --git a/docs/shared/ApiDoc/Context.js b/docs/shared/ApiDoc/Context.js new file mode 100644 index 00000000000..c6861650c92 --- /dev/null +++ b/docs/shared/ApiDoc/Context.js @@ -0,0 +1,6 @@ +import { useMDXComponents } from "@mdx-js/react"; + +export const useApiDocContext = function () { + const MDX = useMDXComponents(); + return MDX.useApiDocContext(this, arguments); +}; diff --git a/docs/shared/ApiDoc/DocBlock.js b/docs/shared/ApiDoc/DocBlock.js new file mode 100644 index 00000000000..28ee46b305a --- /dev/null +++ b/docs/shared/ApiDoc/DocBlock.js @@ -0,0 +1,161 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Stack } from "@chakra-ui/react"; +import { useApiDocContext } from "."; +import { useMDXComponents } from "@mdx-js/react"; + +export function DocBlock({ + canonicalReference, + summary = true, + remarks = false, + example = false, + remarksCollapsible = false, + deprecated = false, + releaseTag = false, +}) { + return ( + + {deprecated && } + {releaseTag && } + {summary && } + {remarks && ( + + )} + {example && } + + ); +} + +DocBlock.propTypes = { + canonicalReference: PropTypes.string.isRequired, + summary: PropTypes.bool, + remarks: PropTypes.bool, + example: PropTypes.bool, + remarksCollapsible: PropTypes.bool, + deprecated: PropTypes.bool, +}; + +function MaybeCollapsible({ collapsible, children }) { + return ( + collapsible ? + children ? +
+ Read more... + {children} +
+ : null + : children + ); +} +MaybeCollapsible.propTypes = { + collapsible: PropTypes.bool, + children: PropTypes.node, +}; + +export function Deprecated({ canonicalReference, collapsible = false }) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + const MDX = useMDXComponents(); + const value = item.comment?.deprecated; + if (!value) return null; + return ( + + +

⚠️ Deprecated

+ {value} +
+
+ ); +} +Deprecated.propTypes = { + canonicalReference: PropTypes.string.isRequired, + collapsible: PropTypes.bool, +}; + +export function Summary({ canonicalReference, collapsible = false }) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + const MDX = useMDXComponents(); + const value = item.comment?.summary; + if (!value) return null; + return ( + + {value && {value}} + + ); +} +Summary.propTypes = { + canonicalReference: PropTypes.string.isRequired, + collapsible: PropTypes.bool, +}; + +export function Remarks({ canonicalReference, collapsible = false }) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + const MDX = useMDXComponents(); + const value = item.comment?.remarks?.replace(/^@remarks/g, ""); + if (!value) return null; + return ( + + {value && {value}} + + ); +} +Remarks.propTypes = { + canonicalReference: PropTypes.string.isRequired, + collapsible: PropTypes.bool, +}; + +export function Example({ + canonicalReference, + collapsible = false, + index = 0, +}) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + const MDX = useMDXComponents(); + const value = item.comment?.examples[index]; + if (!value) return null; + return ( + <> + + {value && {value}} + + + ); +} +Example.propTypes = { + canonicalReference: PropTypes.string.isRequired, + collapsible: PropTypes.bool, + index: PropTypes.number, +}; + +export function ReleaseTag({ canonicalReference }) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + const MDX = useMDXComponents(); + + if (item.releaseTag === "Public") { + return null; + } + + return ( + + This is in{" "} + + {item.releaseTag.toLowerCase()} stage + {" "} + and is subject to breaking changes. + + ); +} + +ReleaseTag.propTypes = { + canonicalReference: PropTypes.string.isRequired, +}; diff --git a/docs/shared/ApiDoc/EnumDetails.js b/docs/shared/ApiDoc/EnumDetails.js new file mode 100644 index 00000000000..7c7587cc0cc --- /dev/null +++ b/docs/shared/ApiDoc/EnumDetails.js @@ -0,0 +1,71 @@ +import { useMDXComponents } from "@mdx-js/react"; + +import PropTypes from "prop-types"; +import React, { useMemo } from "react"; +import { DocBlock, useApiDocContext, ApiDocHeading, SectionHeading } from "."; +import { GridItem, Text } from "@chakra-ui/react"; +import { ResponsiveGrid } from "./ResponsiveGrid"; +import { sortWithCustomOrder } from "./sortWithCustomOrder"; + +export function EnumDetails({ + canonicalReference, + headingLevel, + customOrder = [], +}) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + + const sortedMembers = useMemo( + () => item.members.map(getItem).sort(sortWithCustomOrder(customOrder)), + [item.members, getItem, customOrder] + ); + + return ( + <> + + + + + Enumeration Members + + + + {sortedMembers.map((member) => ( + + + + + ))} + + + ); +} + +EnumDetails.propTypes = { + canonicalReference: PropTypes.string.isRequired, + headingLevel: PropTypes.number.isRequired, + customOrder: PropTypes.arrayOf(PropTypes.string), +}; diff --git a/docs/shared/ApiDoc/Function.js b/docs/shared/ApiDoc/Function.js new file mode 100644 index 00000000000..ca44066eddd --- /dev/null +++ b/docs/shared/ApiDoc/Function.js @@ -0,0 +1,181 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { useMDXComponents } from "@mdx-js/react"; +import { + ApiDocHeading, + SubHeading, + DocBlock, + ParameterTable, + useApiDocContext, + PropertySignatureTable, + SourceLink, + Example, +} from "."; +import { GridItem } from "@chakra-ui/react"; +export function FunctionSignature({ + canonicalReference, + parameterTypes = false, + name = true, + arrow = false, + highlight = false, +}) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const { displayName, parameters, returnType, typeParameters } = + getItem(canonicalReference); + + let paramSignature = parameters + .map((p) => { + let pStr = p.name; + if (p.optional) { + pStr += "?"; + } + if (parameterTypes) { + pStr += ": " + p.type; + } + return pStr; + }) + .join(",\n "); + + if (paramSignature) { + paramSignature = "\n " + paramSignature + "\n"; + } + + const genericSignature = + typeParameters?.length ? + `<${typeParameters.map((p) => p.name).join(", ")}>` + : ""; + + const signature = `${arrow ? "" : "function "}${ + name ? displayName : "" + }${genericSignature}(${paramSignature})${arrow ? " =>" : ":"} ${ + returnType.type + }`; + + return highlight ? + + {signature} + + : signature; +} + +FunctionSignature.propTypes = { + canonicalReference: PropTypes.string.isRequired, + parameterTypes: PropTypes.bool, + name: PropTypes.bool, + arrow: PropTypes.bool, + highlight: PropTypes.bool, +}; + +export function ReturnType({ canonicalReference }) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + + return ( + <> + {item.comment?.returns} + + {item.returnType.type} + + {item.returnType.primaryCanonicalReference?.endsWith(":interface") ? +
+ + Show/hide child attributes + + +
+ : null} + + ); +} +ReturnType.propTypes = { + canonicalReference: PropTypes.string.isRequired, +}; + +export function FunctionDetails({ + canonicalReference, + customParameterOrder, + headingLevel, + result, +}) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + return ( + <> + + + {item.comment?.examples.length == 0 ? null : ( + <> + + Example + + + + )} + + Signature + + + + {item.parameters.length == 0 ? null : ( + <> + + Parameters + + + + )} + {( + result === false || + (result === undefined && item.returnType.type === "void") + ) ? + null + : <> + + Result + + {result || } + } + + ); +} + +FunctionDetails.propTypes = { + canonicalReference: PropTypes.string.isRequired, + headingLevel: PropTypes.number.isRequired, + customParameterOrder: PropTypes.arrayOf(PropTypes.string), + result: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]), +}; diff --git a/docs/shared/ApiDoc/Heading.js b/docs/shared/ApiDoc/Heading.js new file mode 100644 index 00000000000..fea39f5c9f4 --- /dev/null +++ b/docs/shared/ApiDoc/Heading.js @@ -0,0 +1,117 @@ +import { useMDXComponents } from "@mdx-js/react"; +import PropTypes from "prop-types"; +import React from "react"; +import { Box, Text } from "@chakra-ui/react"; +import { FunctionSignature } from "."; +import { useApiDocContext } from "./Context"; + +export function Heading({ headingLevel, children, as, minVersion, ...props }) { + const MDX = useMDXComponents(); + let heading = children; + + if (as != undefined && headingLevel != undefined) { + throw new Error( + "Heading: Cannot specify both `as` and `headingLevel` at the same time." + ); + } + const Tag = as ? as : MDX[`h${headingLevel}`]; + + return ( + + {heading} + {minVersion ? + + : null} + + ); +} +Heading.propTypes = { + headingLevel: PropTypes.number, + children: PropTypes.node.isRequired, + id: PropTypes.string, + as: PropTypes.any, + minVersion: PropTypes.string, +}; + +export function SubHeading({ canonicalReference, headingLevel, ...props }) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + + return ( + + ); +} +SubHeading.propTypes = { + ...Heading.propTypes, + canonicalReference: PropTypes.string.isRequired, +}; + +export function ApiDocHeading({ + canonicalReference, + headingLevel, + signature = false, + since = false, + prefix = "", + suffix = "", + ...props +}) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + let heading = + ( + signature && + (item.kind === "MethodSignature" || + item.kind === "Function" || + item.kind === "Method") + ) ? + + : {item.displayName}; + + return ( + + + {prefix} + {heading} + {suffix} + + + ); +} +ApiDocHeading.propTypes = { + canonicalReference: PropTypes.string.isRequired, + headingLevel: PropTypes.number, + signature: PropTypes.bool, + since: PropTypes.bool, + prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), +}; + +export function SectionHeading(props) { + return ( + + ); +} +SectionHeading.propTypes = Text.propTypes; diff --git a/docs/shared/ApiDoc/InterfaceDetails.js b/docs/shared/ApiDoc/InterfaceDetails.js new file mode 100644 index 00000000000..92c5ae7ae55 --- /dev/null +++ b/docs/shared/ApiDoc/InterfaceDetails.js @@ -0,0 +1,45 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { GridItem } from "@chakra-ui/react"; +import { + ApiDocHeading, + DocBlock, + PropertySignatureTable, + useApiDocContext, + SectionHeading, +} from "."; +export function InterfaceDetails({ + canonicalReference, + headingLevel, + link, + customPropertyOrder, +}) { + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + return ( + <> + + + + Properties + + + + ); +} + +InterfaceDetails.propTypes = { + canonicalReference: PropTypes.string.isRequired, + headingLevel: PropTypes.number.isRequired, + link: PropTypes.bool, + customPropertyOrder: PropTypes.arrayOf(PropTypes.string), +}; diff --git a/docs/shared/ApiDoc/ParameterTable.js b/docs/shared/ApiDoc/ParameterTable.js new file mode 100644 index 00000000000..40be1a6f8f1 --- /dev/null +++ b/docs/shared/ApiDoc/ParameterTable.js @@ -0,0 +1,83 @@ +import { useMDXComponents } from "@mdx-js/react"; + +import PropTypes from "prop-types"; +import React from "react"; +import { GridItem, Text } from "@chakra-ui/react"; +import { PropertySignatureTable, useApiDocContext } from "."; +import { ResponsiveGrid } from "./ResponsiveGrid"; + +export function ParameterTable({ canonicalReference }) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + + if (item.parameters.length === 0) return null; + + return ( + <> + + Name / Type + Description + + {item.parameters.map((parameter) => { + const interfaceReference = + parameter.primaryCanonicalReference?.endsWith(":interface") ? + parameter.primaryCanonicalReference + : undefined; + const id = `${item.displayName.toLowerCase()}-parameters-${parameter.name.toLowerCase()}`; + + return ( + + + + + {parameter.name} + {parameter.optional ? + (optional) + : null} + + + + {parameter.type} + + + + {parameter.comment && ( + {parameter.comment} + )} + + {interfaceReference && ( +
+ + Show/hide child attributes + + +
+ )} +
+ ); + })} +
+ + ); +} + +ParameterTable.propTypes = { + canonicalReference: PropTypes.string.isRequired, + showHeaders: PropTypes.bool, +}; diff --git a/docs/shared/ApiDoc/PropertyDetails.js b/docs/shared/ApiDoc/PropertyDetails.js new file mode 100644 index 00000000000..e8c0f28faae --- /dev/null +++ b/docs/shared/ApiDoc/PropertyDetails.js @@ -0,0 +1,27 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { ApiDocHeading, DocBlock } from "."; + +export function PropertyDetails({ canonicalReference, headingLevel }) { + return ( + <> + + + + ); +} + +PropertyDetails.propTypes = { + canonicalReference: PropTypes.string.isRequired, + headingLevel: PropTypes.number.isRequired, +}; diff --git a/docs/shared/ApiDoc/PropertySignatureTable.js b/docs/shared/ApiDoc/PropertySignatureTable.js new file mode 100644 index 00000000000..0ccbeb4a9af --- /dev/null +++ b/docs/shared/ApiDoc/PropertySignatureTable.js @@ -0,0 +1,150 @@ +import { useMDXComponents } from "@mdx-js/react"; + +import PropTypes from "prop-types"; +import React, { useMemo } from "react"; +import { + DocBlock, + FunctionSignature, + useApiDocContext, + ApiDocHeading, +} from "."; +import { GridItem, Text } from "@chakra-ui/react"; +import { ResponsiveGrid } from "./ResponsiveGrid"; +import { groupItems } from "./sortWithCustomOrder"; + +export function PropertySignatureTable({ + canonicalReference, + prefix = "", + showHeaders = false, + display = "parent", + customOrder = [], + idPrefix = "", + genericNames, +}) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + + const Wrapper = display === "parent" ? ResponsiveGrid : React.Fragment; + const groupedProperties = useMemo( + () => groupItems(item.properties.map(getItem), customOrder), + [item.properties, getItem, customOrder] + ); + if (item.childrenIncomplete) { + console.warn( + "Warning: some properties might be missing from the table due to complex inheritance!", + item.childrenIncompleteDetails + ); + } + + const replaceGenericNames = React.useMemo(() => { + if (!genericNames) return (str) => str; + + const replacements = {}; + item.typeParameters.forEach((p, i) => { + if (genericNames[i] === p.name) return; + replacements[p.name] = genericNames[i]; + }); + if (!Object.values(replacements).length) return (str) => str; + + const genericReplacementRegex = new RegExp( + `\\b(${Object.keys(replacements).join("|")})\\b`, + "g" + ); + function replace(match) { + return replacements[match] || match; + } + return function replaceGenericNames(str) { + return str.replace(genericReplacementRegex, replace); + }; + }); + + return ( + <> + {item.childrenIncomplete ? + +
+ (Warning: some properties might be missing from the table due to + complex inheritance!) +
+ : null} + + + {showHeaders ? + <> + Name / Type + Description + + : null} + {Object.entries(groupedProperties).map( + ([groupName, sortedProperties]) => ( + + {groupName ? + {groupName} + : null} + {sortedProperties.map((property) => ( + + + + {prefix} + + : null + } + suffix={property.optional ? (optional) : null} + id={ + idPrefix ? + `${idPrefix}-${property.displayName.toLowerCase()}` + : undefined + } + /> + + {property.kind === "MethodSignature" ? + + : replaceGenericNames(property.type)} + + + + + + + ))} + + ) + )} + + + ); +} + +PropertySignatureTable.propTypes = { + canonicalReference: PropTypes.string.isRequired, + idPrefix: PropTypes.string.isRequired, + prefix: PropTypes.string, + showHeaders: PropTypes.bool, + display: PropTypes.oneOf(["parent", "child"]), + customOrder: PropTypes.arrayOf(PropTypes.string), + genericNames: PropTypes.arrayOf(PropTypes.string), +}; diff --git a/docs/shared/ApiDoc/ResponsiveGrid.js b/docs/shared/ApiDoc/ResponsiveGrid.js new file mode 100644 index 00000000000..2f7b1b93931 --- /dev/null +++ b/docs/shared/ApiDoc/ResponsiveGrid.js @@ -0,0 +1,81 @@ +import React from "react"; +import { Global } from "@emotion/react"; +import { Grid } from "@chakra-ui/react"; + +/** + * This component is actually over in the docs repo, just repeated here so the + * styles are visible. + */ +export function ResponsiveGridStyles() { + return ( + *, .responsive-grid > details > *": { + background: "var(--chakra-colors-bg)", + }, + ".responsive-grid .cell, .responsive-grid .row": { + padding: "var(--chakra-space-4)", + }, + ".responsive-grid details .first.cell + .cell": { + marginTop: -1, + paddingTop: 0, + }, + ".responsive-grid details h6": { + display: "inline", + }, + ".responsive-grid .heading": { + fontFamily: "var(--chakra-fonts-heading)", + fontWeight: "var(--chakra-fontWeights-normal)", + textTransform: "uppercase", + letterSpacing: "var(--chakra-letterSpacings-wider)", + fontSize: "var(--chakra-fontSizes-xs)", + }, + }} + /> + ); +} + +export function ResponsiveGrid({ children, columns = 2 }) { + /* + responsiveness not regarding screen width, but actual available space: + if less than 350px, show only one column + show at most two columns (that's where the 45% hack comes - 45% * 3 won't fit) + */ + + return ( + + {children} + + ); +} diff --git a/docs/shared/ApiDoc/SourceLink.js b/docs/shared/ApiDoc/SourceLink.js new file mode 100644 index 00000000000..5e1e1da58b4 --- /dev/null +++ b/docs/shared/ApiDoc/SourceLink.js @@ -0,0 +1,24 @@ +import { useMDXComponents } from "@mdx-js/react"; +import PropTypes from "prop-types"; +import React from "react"; +import { Text } from "@chakra-ui/react"; +import { useApiDocContext } from "./Context"; + +export function SourceLink({ canonicalReference }) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + const item = getItem(canonicalReference); + return item.file ? + + + ({item.file}) + + + : null; +} +SourceLink.propTypes = { + canonicalReference: PropTypes.string.isRequired, +}; diff --git a/docs/shared/ApiDoc/Tuple.js b/docs/shared/ApiDoc/Tuple.js new file mode 100644 index 00000000000..3ab881151b3 --- /dev/null +++ b/docs/shared/ApiDoc/Tuple.js @@ -0,0 +1,68 @@ +import React from "react"; +import { useMDXComponents } from "@mdx-js/react"; +import { useApiDocContext, PropertySignatureTable } from "."; +import PropTypes from "prop-types"; + +export function ManualTuple({ elements = [], idPrefix = "" }) { + const MDX = useMDXComponents(); + const getItem = useApiDocContext(); + + return ( + + + + Name + Type + Description + + + + {elements.map( + ({ name, type, description, canonicalReference }, idx) => { + const item = getItem(canonicalReference); + const separatorStyle = item ? { borderBottom: 0 } : {}; + return ( + + + {name} + + {type} + + {description} + + {item ? + + +
+ Show/hide child attributes + +
+
+
+ : null} +
+ ); + } + )} +
+
+ ); +} +ManualTuple.propTypes = { + elements: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, + description: PropTypes.oneOfType([PropTypes.node, PropTypes.string]) + .isRequired, + canonicalReference: PropTypes.string, + }) + ).isRequired, +}; diff --git a/docs/shared/ApiDoc/index.js b/docs/shared/ApiDoc/index.js new file mode 100644 index 00000000000..87e85357bbf --- /dev/null +++ b/docs/shared/ApiDoc/index.js @@ -0,0 +1,18 @@ +export { useApiDocContext } from "./Context"; +export { + DocBlock, + Deprecated, + Example, + Remarks, + ReleaseTag, + Summary, +} from "./DocBlock"; +export { PropertySignatureTable } from "./PropertySignatureTable"; +export { ApiDocHeading, SubHeading, SectionHeading } from "./Heading"; +export { InterfaceDetails } from "./InterfaceDetails"; +export { FunctionSignature, FunctionDetails } from "./Function"; +export { ParameterTable } from "./ParameterTable"; +export { PropertyDetails } from "./PropertyDetails"; +export { EnumDetails } from "./EnumDetails"; +export { ManualTuple } from "./Tuple"; +export { SourceLink } from "./SourceLink"; diff --git a/docs/shared/ApiDoc/sortWithCustomOrder.js b/docs/shared/ApiDoc/sortWithCustomOrder.js new file mode 100644 index 00000000000..c2cd411e304 --- /dev/null +++ b/docs/shared/ApiDoc/sortWithCustomOrder.js @@ -0,0 +1,71 @@ +/** + * Sorts items by their `displayName` with a custom order: + * - items within the `customOrder` array will be sorted to the start, + * sorted by the order of the `customOrder` array + * - items not in the `customOrder` array will be sorted in lexicographical order after that + * - deprecated items will be sorted in lexicographical order to the end + */ +export function sortWithCustomOrder(customOrder = []) { + return (a, b) => { + let aIndex = customOrder.indexOf(a.displayName); + if (aIndex == -1) { + aIndex = + a.comment?.deprecated ? + Number.MAX_SAFE_INTEGER + : Number.MAX_SAFE_INTEGER - 1; + } + let bIndex = customOrder.indexOf(b.displayName); + if (bIndex == -1) { + bIndex = + b.comment?.deprecated ? + Number.MAX_SAFE_INTEGER + : Number.MAX_SAFE_INTEGER - 1; + } + if (aIndex === bIndex) { + return sortLocally(a.displayName, b.displayName); + } else { + return aIndex - bIndex; + } + }; +} + +function sortLocally(a, b) { + return a.localeCompare(b); +} + +/** + * + * @param {Array<{displayName: string, comment: { docGroup: string }}>} items + * @param {string[]} customOrder + */ +export function groupItems(items = [], customOrder = []) { + const customItems = []; + const groupedItems = []; + for (const item of items) { + if (customOrder.includes(item.displayName)) customItems.push(item); + else groupedItems.push(item); + } + customItems.sort(sortWithCustomOrder(customOrder)); + const groupNames = [ + ...new Set(groupedItems.map((item) => item.comment?.docGroup || "Other")), + ].sort(sortLocally); + const groups = Object.fromEntries(groupNames.map((name) => [name, []])); + for (const item of groupedItems) { + groups[item.comment?.docGroup || "Other"].push(item); + } + for (const group of Object.values(groups)) { + group.sort(sortWithCustomOrder([])); + } + const groupsWithoutPrefix = Object.fromEntries( + Object.entries(groups).map(([name, items]) => [ + name.replace(/^\s*\d*\.\s*/, ""), + items, + ]) + ); + return customItems.length === 0 ? + groupsWithoutPrefix + : { + "": customItems, + ...groupsWithoutPrefix, + }; +} diff --git a/docs/shared/Overrides/UseLoadableQueryResult.js b/docs/shared/Overrides/UseLoadableQueryResult.js new file mode 100644 index 00000000000..78c45ced062 --- /dev/null +++ b/docs/shared/Overrides/UseLoadableQueryResult.js @@ -0,0 +1,58 @@ +import React from "react"; +import { useMDXComponents } from "@mdx-js/react"; +import { ManualTuple } from "../ApiDoc"; + +const HANDLERS = `{ + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; + reset: ResetFunction; +}`; + +const RETURN_VALUE = `[ + loadQuery: LoadQueryFunction, + queryRef: QueryRef | null, + { + fetchMore: FetchMoreFunction; + refetch: RefetchFunction; + reset: ResetFunction; + } +]`; + +export function UseLoadableQueryResult() { + const MDX = useMDXComponents(); + + return ( +
+ + {RETURN_VALUE} + + A tuple of three values: + ", + description: + "A function used to imperatively load a query. Calling this function will create or update the `queryRef` returned by `useLoadableQuery`, which should be passed to `useReadQuery`.", + }, + { + name: "queryRef", + type: "QueryRef | null", + description: + "The `queryRef` used by `useReadQuery` to read the query result.", + canonicalReference: "@apollo/client!QueryRef:interface", + }, + { + name: "handlers", + description: + "Additional handlers used for the query, such as `refetch`.", + type: HANDLERS, + }, + ]} + /> +
+ ); +} + +UseLoadableQueryResult.propTypes = {}; diff --git a/docs/shared/apollo-provider.mdx b/docs/shared/apollo-provider.mdx deleted file mode 100644 index 8b137891791..00000000000 --- a/docs/shared/apollo-provider.mdx +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/shared/document-transform-options.mdx b/docs/shared/document-transform-options.mdx deleted file mode 100644 index 3675c19cfdf..00000000000 --- a/docs/shared/document-transform-options.mdx +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `getCacheKey` - -`(document: DocumentNode) => any[] | undefined` - - -Defines a custom cache key for a GraphQL document that will determine whether to re-run the document transform when given the same input GraphQL document. Returns an array that defines the cache key. Return `undefined` to disable caching for that GraphQL document. - -> **Note:** The items in the array may be any type, but also need to be referentially stable to guarantee a stable cache key. - -The default implementation of this function returns the `document` as the cache key. -
- -###### `cache` - -`boolean` - - -Determines whether to cache the transformed GraphQL document. Caching can speed up repeated calls to the document transform for the same input document. Set to `false` to completely disable caching for the document transform. When disabled, this option takes precedence over the [`getCacheKey`](#getcachekey) option. - -The default value is `true`. -
- diff --git a/docs/shared/mutation-options.mdx b/docs/shared/mutation-options.mdx deleted file mode 100644 index fa22a6e6010..00000000000 --- a/docs/shared/mutation-options.mdx +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -**Operation options** - -
- -###### `mutation` - -`DocumentNode` - - -A GraphQL query string parsed into an AST with the `gql` template literal. - -**Optional** for the `useMutation` hook, because the mutation can also be provided as the first parameter to the hook. - -**Required** for the `Mutation` component. -
- -###### `variables` - -`{ [key: string]: any }` - - -An object containing all of the GraphQL variables your mutation requires to execute. - -Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value. - -
- -###### `errorPolicy` - -`ErrorPolicy` - - -Specifies how the mutation handles a response that returns both GraphQL errors and partial results. - -For details, see [GraphQL error policies](/react/data/error-handling/#graphql-error-policies). - -The default value is `none`, meaning that the mutation result includes error details but _not_ partial results. - -
- -###### `onCompleted` - -`(data?: TData, clientOptions?: BaseMutationOptions) => void` - - -A callback function that's called when your mutation successfully completes with zero errors (or if `errorPolicy` is `ignore` and partial data is returned). - -This function is passed the mutation's result `data` and any options passed to the mutation. - -
- -###### `onError` - -`(error: ApolloError, clientOptions?: BaseMutationOptions) => void` - - -A callback function that's called when the mutation encounters one or more errors (unless `errorPolicy` is `ignore`). - -This function is passed an [`ApolloError`](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/errors/index.ts#L36-L39) object that contains either a `networkError` object or a `graphQLErrors` array, depending on the error(s) that occurred, as well as any options passed the mutation. - -
- -###### `onQueryUpdated` - -`(observableQuery: ObservableQuery, diff: Cache.DiffResult, lastDiff: Cache.DiffResult | undefined) => boolean | TResult` - - - -Optional callback for intercepting queries whose cache data has been updated by the mutation, as well as any queries specified in the [`refetchQueries: [...]`](#refetchQueries) list passed to `client.mutate`. - -Returning a `Promise` from `onQueryUpdated` will cause the final mutation `Promise` to await the returned `Promise`. Returning `false` causes the query to be ignored. - -
- -###### `refetchQueries` - -`Array | ((mutationResult: FetchResult) => Array)` - - -An array (or a function that _returns_ an array) that specifies which queries you want to refetch after the mutation occurs. - -Each array value can be either: - -* An object containing the `query` to execute, along with any `variables` -* A string indicating the operation name of the query to refetch - -
- -###### `awaitRefetchQueries` - -`boolean` - - -If `true`, makes sure all queries included in `refetchQueries` are completed before the mutation is considered complete. - -The default value is `false` (queries are refetched asynchronously). - -
- -###### `ignoreResults` - -`boolean` - - -If `true`, the mutation's `data` property is not updated with the mutation's result. - -The default value is `false`. - -
- -**Networking options** - -
- -###### `notifyOnNetworkStatusChange` - -`boolean` - - -If `true`, the in-progress mutation's associated component re-renders whenever the network status changes or a network error occurs. - -The default value is `false`. - -
- -###### `client` - -`ApolloClient` - - -The instance of `ApolloClient` to use to execute the mutation. - -By default, the instance that's passed down via context is used, but you can provide a different instance here. - -
- -###### `context` - -`Record` - - -If you're using [Apollo Link](/react/api/link/introduction/), this object is the initial value of the `context` object that's passed along your link chain. - -
- -**Caching options** - -
- -###### `update` - -`(cache: ApolloCache, mutationResult: FetchResult) => void` - - -A function used to update the Apollo Client cache after the mutation completes. - -For more information, see [Updating the cache after a mutation](/react/data/mutations#updating-the-cache-after-a-mutation). - -
- -###### `optimisticResponse` - -`Object` - - -If provided, Apollo Client caches this temporary (and potentially incorrect) response until the mutation completes, enabling more responsive UI updates. - -For more information, see [Optimistic mutation results](/react/performance/optimistic-ui/). - -
- -###### `fetchPolicy` - -`MutationFetchPolicy` - - -Provide `no-cache` if the mutation's result should _not_ be written to the Apollo Client cache. - -The default value is `network-only` (which means the result _is_ written to the cache). - -Unlike queries, mutations _do not_ support [fetch policies](/react/data/queries/#setting-a-fetch-policy) besides `network-only` and `no-cache`. - -
diff --git a/docs/shared/mutation-result.mdx b/docs/shared/mutation-result.mdx deleted file mode 100644 index 21e4e858137..00000000000 --- a/docs/shared/mutation-result.mdx +++ /dev/null @@ -1,145 +0,0 @@ -**Mutate function:** - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `mutate` - -`(options?: MutationOptions) => Promise` - - -A function to trigger the mutation from your UI. You can optionally pass this function any of the following options: - -* `awaitRefetchQueries` -* `context` -* `fetchPolicy` -* `onCompleted` -* `onError` -* `optimisticResponse` -* `refetchQueries` -* `onQueryUpdated` -* `update` -* `variables` -* `client` - -Any option you pass here overrides any existing value for that option that you passed to `useMutation`. - -The mutate function returns a promise that fulfills with your mutation result. -
- -**Mutation result:** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -###### `data` - -`TData` - - -The data returned from your mutation. Can be `undefined` if `ignoreResults` is `true`. -
- -###### `loading` - -`boolean` - - -If `true`, the mutation is currently in flight. -
- -###### `error` - -`ApolloError` - - -If the mutation produces one or more errors, this object contains either an array of `graphQLErrors` or a single `networkError`. Otherwise, this value is `undefined`. - -For more information, see [Handling operation errors](/react/data/error-handling/). - -
- -###### `called` - -`boolean` - - -If `true`, the mutation's mutate function has been called. - -
- -###### `client` - -`ApolloClient` - - -The instance of Apollo Client that executed the mutation. - -Can be useful for manually executing followup operations or writing data to the cache. - -
- -###### `reset` - -`() => void` - - -A function that you can call to reset the mutation's result to its initial, uncalled state. - -
diff --git a/docs/shared/query-options.mdx b/docs/shared/query-options.mdx deleted file mode 100644 index 2a265ada2da..00000000000 --- a/docs/shared/query-options.mdx +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -**Operation options** - -
- -###### `query` - -`DocumentNode` - - -A GraphQL query string parsed into an AST with the `gql` template literal. - -**Optional** for the `useQuery` hook, because the query can be provided as the first parameter to the hook. **Required** for the `Query` component. -
- -###### `variables` - -`{ [key: string]: any }` - - -An object containing all of the GraphQL variables your query requires to execute. - -Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value. - -
- -###### `errorPolicy` - -`ErrorPolicy` - - -Specifies how the query handles a response that returns both GraphQL errors and partial results. - -For details, see [GraphQL error policies](/react/data/error-handling/#graphql-error-policies). - -The default value is `none`, meaning that the query result includes error details but _not_ partial results. - -
- -###### `onCompleted` - -`(data: TData | {}) => void` - - -A callback function that's called when your query successfully completes with zero errors (or if `errorPolicy` is `ignore` and partial data is returned). - -This function is passed the query's result `data`. - -
- -###### `onError` - -`(error: ApolloError) => void` - - -A callback function that's called when the query encounters one or more errors (unless `errorPolicy` is `ignore`). - -This function is passed an [`ApolloError`](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/errors/index.ts#L36-L39) object that contains either a `networkError` object or a `graphQLErrors` array, depending on the error(s) that occurred. - -
- -###### `skip` - -`boolean` - - -If `true`, the query is _not_ executed. **Not available with `useLazyQuery`.** - -This property is part of Apollo Client's React integration, and it is _not_ available in the [core `ApolloClient` API](/react/api/core/ApolloClient/). - -The default value is `false`. - -
- -**Networking options** - -
- -###### `pollInterval` - -`number` - - -Specifies the interval (in milliseconds) at which the query polls for updated results. - -The default value is `0` (no polling). - -
- -###### `notifyOnNetworkStatusChange` - -`boolean` - - -If `true`, the in-progress query's associated component re-renders whenever the network status changes or a network error occurs. - -The default value is `false`. - -
- -###### `context` - -`Record` - - -If you're using [Apollo Link](/react/api/link/introduction/), this object is the initial value of the `context` object that's passed along your link chain. - -
- -###### `ssr` - -`boolean` - - -Pass `false` to skip executing the query during [server-side rendering](/react/performance/server-side-rendering/). - -
- -###### `client` - -`ApolloClient` - - -The instance of `ApolloClient` to use to execute the query. - -By default, the instance that's passed down via context is used, but you can provide a different instance here. - -
- -**Caching options** - -
- -###### `fetchPolicy` - -`FetchPolicy` - - -Specifies how the query interacts with the Apollo Client cache during execution (for example, whether it checks the cache for results before sending a request to the server). - -For details, see [Setting a fetch policy](/react/data/queries/#setting-a-fetch-policy). - -The default value is `cache-first`. - -
- -###### `nextFetchPolicy` - -`FetchPolicy` - - -Specifies the [`fetchPolicy`](#fetchpolicy) to use for all executions of this query _after_ this execution. - -For example, you can use this to switch back to a `cache-first` fetch policy after using `cache-and-network` or `network-only` for a single execution. - -
- -###### `returnPartialData` - -`boolean` - - -If `true`, the query can return _partial_ results from the cache if the cache doesn't contain results for _all_ queried fields. - -The default value is `false`. - -
- -**Deprecated options** - -
- -###### `partialRefetch` - -`boolean` - - -**Deprecated.** If `true`, causes a query `refetch` if the query result is detected as partial. Setting this option is unnecessary in Apollo Client 3, thanks to a more consistent application of fetch policies. It might be removed in a future release. - -The default value is `false`. - -
diff --git a/docs/shared/query-result.mdx b/docs/shared/query-result.mdx deleted file mode 100644 index aed5e2f478f..00000000000 --- a/docs/shared/query-result.mdx +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -**Operation data** - -
- -###### `data` - -`TData` - - -An object containing the result of your GraphQL query after it completes. - -This value might be `undefined` if a query results in one or more errors (depending on the query's `errorPolicy`). - -
- -###### `previousData` - -`TData` - - -An object containing the result from the most recent _previous_ execution of this query. - -This value is `undefined` if this is the query's first execution. - -
- -###### `error` - -`ApolloError` - - -If the query produces one or more errors, this object contains either an array of `graphQLErrors` or a single `networkError`. Otherwise, this value is `undefined`. - -For more information, see [Handling operation errors](/react/data/error-handling/). - -
- -###### `variables` - -`{ [key: string]: any }` - - -An object containing the variables that were provided for the query. - -
- -**Network info** - -
- -###### `loading` - -`boolean` - - -If `true`, the query is still in flight and results have not yet been returned. - -
- -###### `networkStatus` - -`NetworkStatus` - - -A number indicating the current network state of the query's associated request. [See possible values.](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/core/networkStatus.ts#L4) - -Used in conjunction with the [`notifyOnNetworkStatusChange`](#notifyonnetworkstatuschange) option. - -
- -###### `client` - -`ApolloClient` - - -The instance of Apollo Client that executed the query. - -Can be useful for manually executing followup queries or writing data to the cache. - -
- -###### `called` - -`boolean` - - -If `true`, the associated lazy query has been executed. - -This field is only present on the result object returned by [`useLazyQuery`](/react/data/queries/#executing-queries-manually). - -
- -**Helper functions** - -
- -###### `refetch` - -`(variables?: Partial) => Promise` - - -A function that enables you to re-execute the query, optionally passing in new `variables`. - -To guarantee that the refetch performs a network request, its `fetchPolicy` is set to `network-only` (unless the original query's `fetchPolicy` is `no-cache` or `cache-and-network`, which also guarantee a network request). - -See also [Refetching](/react/data/queries/#refetching). - -
- -###### `fetchMore` - -`({ query?: DocumentNode, variables?: TVariables, updateQuery: Function}) => Promise` - - -A function that helps you fetch the next set of results for a [paginated list field](/react/pagination/core-api/). - -
- -###### `startPolling` - -`(interval: number) => void` - - -A function that instructs the query to begin re-executing at a specified interval (in milliseconds). - -
- -###### `stopPolling` - -`() => void` - - -A function that instructs the query to stop polling after a previous call to `startPolling`. - -
- -###### `subscribeToMore` - -`(options: { document: DocumentNode, variables?: TVariables, updateQuery?: Function, onError?: Function}) => () => void` - - -A function that enables you to execute a [subscription](/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query. - -This function returns _another_ function that you can call to terminate the subscription. - -
- -###### `updateQuery` - -`(mapFn: (previousResult: TData, options: { variables: TVariables }) => TData) => void` - - -A function that enables you to update the query's cached result without executing a followup GraphQL operation. -See [using updateQuery and updateFragment](/react/caching/cache-interaction/#using-updatequery-and-updatefragment) for additional information. -
diff --git a/docs/shared/subscription-options.mdx b/docs/shared/subscription-options.mdx deleted file mode 100644 index 00148fa2fce..00000000000 --- a/docs/shared/subscription-options.mdx +++ /dev/null @@ -1,14 +0,0 @@ -| Option | Type | Description | -| - | - | - | -| `subscription` | DocumentNode | A GraphQL subscription document parsed into an AST by `graphql-tag`. **Optional** for the `useSubscription` Hook since the subscription can be passed in as the first parameter to the Hook. **Required** for the `Subscription` component. | -| `variables` | { [key: string]: any } | An object containing all of the variables your subscription needs to execute | -| `shouldResubscribe` | boolean | Determines if your subscription should be unsubscribed and subscribed again when an input to the hook (such as `subscription` or `variables`) changes. | -| `skip` | boolean | Determines if the current subscription should be skipped. Useful if, for example, variables depend on previous queries and are not ready yet. | -| `onSubscriptionData` | **Deprecated.** (options: OnSubscriptionDataOptions<TData>) => any | Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives data. The callback `options` object param consists of the current Apollo Client instance in `client`, and the received subscription data in `subscriptionData`. | -| `onData` | (options: OnDataOptions<TData>) => any | Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives data. The callback `options` object param consists of the current Apollo Client instance in `client`, and the received subscription data in `data`. | -| `onError` | (error: ApolloError) => void | Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives an error. | -| `onSubscriptionComplete` | **Deprecated.** () => void | Allows the registration of a callback function that will be triggered when the `useSubscription` Hook / `Subscription` component completes the subscription. | -| `onComplete` | () => void | Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component completes the subscription. | -| `fetchPolicy` | FetchPolicy | How you want your component to interact with the Apollo cache. For details, see [Setting a fetch policy](/react/data/queries/#setting-a-fetch-policy). | -| `context` | Record<string, any> | Shared context between your component and your network interface (Apollo Link). | -| `client` | ApolloClient | An `ApolloClient` instance. By default `useSubscription` / `Subscription` uses the client passed down via context, but a different client can be passed in. | diff --git a/docs/shared/subscription-result.mdx b/docs/shared/subscription-result.mdx deleted file mode 100644 index be7d5295613..00000000000 --- a/docs/shared/subscription-result.mdx +++ /dev/null @@ -1,5 +0,0 @@ -| Property | Type | Description | -| - | - | - | -| `data` | TData | An object containing the result of your GraphQL subscription. Defaults to an empty object. | -| `loading` | boolean | A boolean that indicates whether any initial data has been returned | -| `error` | ApolloError | A runtime error with `graphQLErrors` and `networkError` properties | diff --git a/docs/shared/useBackgroundQuery-options.mdx b/docs/shared/useBackgroundQuery-options.mdx index eb0157d6fdd..d00042a9c98 100644 --- a/docs/shared/useBackgroundQuery-options.mdx +++ b/docs/shared/useBackgroundQuery-options.mdx @@ -89,6 +89,10 @@ If you're using [Apollo Link](/react/api/link/introduction/), this object is the +> **⚠️ Deprecated**: +> Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. +> A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + If `true`, result objects read from the cache will be _canonized_, which means deeply-equal objects will also be `===` (literally the same object), allowing much more efficient comparison of past/present results. The default value is `false`. diff --git a/docs/shared/useBackgroundQuery-result.mdx b/docs/shared/useBackgroundQuery-result.mdx index 4abe01ef38e..529ce5e0b59 100644 --- a/docs/shared/useBackgroundQuery-result.mdx +++ b/docs/shared/useBackgroundQuery-result.mdx @@ -22,7 +22,7 @@ ###### `queryRef` -`QueryReference` +`QueryRef` diff --git a/docs/shared/useFragment-options.mdx b/docs/shared/useFragment-options.mdx index e3ca1c63e3b..75a9be37b48 100644 --- a/docs/shared/useFragment-options.mdx +++ b/docs/shared/useFragment-options.mdx @@ -108,6 +108,10 @@ Each key in the object corresponds to a variable name, and that key's value corr +> **⚠️ Deprecated**: +> Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. +> A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + If `true`, result objects read from the cache will be _canonized_, which means deeply-equal objects will also be `===` (literally the same object), allowing much more efficient comparison of past/present results. The default value is `false`. diff --git a/docs/shared/useSuspenseQuery-options.mdx b/docs/shared/useSuspenseQuery-options.mdx deleted file mode 100644 index c87dc25c544..00000000000 --- a/docs/shared/useSuspenseQuery-options.mdx +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name /
Type
Description
- -**Operation options** - -
- -###### `variables` - -`{ [key: string]: any }` - - -An object containing all of the GraphQL variables your query requires to execute. - -Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value. - -
- -###### `errorPolicy` - -`ErrorPolicy` - - -Specifies how the query handles a response that returns both GraphQL errors and partial results. - -For details, see [GraphQL error policies](/react/data/error-handling/#graphql-error-policies). - -The default value is `none`, which causes the hook to throw the error. -
- -**Networking options** - -
- -###### `context` - -`Record` - - -If you're using [Apollo Link](/react/api/link/introduction/), this object is the initial value of the `context` object that's passed along your link chain. - -
- -###### `canonizeResults` - -`Boolean` - - -If `true`, result objects read from the cache will be _canonized_, which means deeply-equal objects will also be `===` (literally the same object), allowing much more efficient comparison of past/present results. - -The default value is `false`. - -
- -###### `client` - -`ApolloClient` - - -The instance of `ApolloClient` to use to execute the query. - -By default, the instance that's passed down via context is used, but you can provide a different instance here. - -
- -###### `queryKey` - -`string | number | any[]` - - -A unique identifier for the query. Each item in the array must be a stable identifier to prevent infinite fetches. - -This is useful when using the same query and variables combination in more than one component, otherwise the components may clobber each other. This can also be used to force the query to re-evaluate fresh. - -
- -**Caching options** - -
- -###### `fetchPolicy` - -`SuspenseQueryHookFetchPolicy` - - -Specifies how the query interacts with the Apollo Client cache during execution (for example, whether it checks the cache for results before sending a request to the server). - -For details, see [Setting a fetch -policy](/react/data/queries/#setting-a-fetch-policy). This hook only supports -the `cache-first`, `network-only`, `no-cache`, and `cache-and-network` fetch -policies. - -The default value is `cache-first`. - -
- -###### `returnPartialData` - -`boolean` - - -If `true`, the query can return _partial_ results from the cache if the cache doesn't contain results for _all_ queried fields. - -The default value is `false`. - -
- -###### `refetchWritePolicy` - -`"merge" | "overwrite"` - - -Watched queries must opt into overwriting existing data on refetch, by passing `refetchWritePolicy: "overwrite"` in their `WatchQueryOptions`. - -The default value is `"overwrite"`. - -
- -###### `skip` (deprecated) - -`boolean` - - -If `true`, the query is not executed. The default value is `false`. - -This option is deprecated and only supported to ease the migration from `useQuery`. It will be removed in a future release. -Please use [`skipToken`](/react/api/react/hooks#skiptoken`) instead of the `skip` option as it is more type-safe. - -
diff --git a/docs/shared/useSuspenseQuery-result.mdx b/docs/shared/useSuspenseQuery-result.mdx index 36cd9a08e7b..71ad1f74ad3 100644 --- a/docs/shared/useSuspenseQuery-result.mdx +++ b/docs/shared/useSuspenseQuery-result.mdx @@ -111,7 +111,7 @@ A function that enables you to re-execute the query, optionally passing in new ` To guarantee that the refetch performs a network request, its `fetchPolicy` is set to `network-only` (unless the original query's `fetchPolicy` is `no-cache` or `cache-and-network`, which also guarantee a network request). -Calling this function will cause the component to re-suspend, , unless the call site is wrapped in [`startTransition`](https://react.dev/reference/react/startTransition). +Calling this function will cause the component to re-suspend, unless the call site is wrapped in [`startTransition`](https://react.dev/reference/react/startTransition). diff --git a/docs/source/api/cache/InMemoryCache.mdx b/docs/source/api/cache/InMemoryCache.mdx index 0bd104013ee..4891eef7b01 100644 --- a/docs/source/api/cache/InMemoryCache.mdx +++ b/docs/source/api/cache/InMemoryCache.mdx @@ -2,8 +2,12 @@ title: class InMemoryCache description: API reference api_reference: true +api_doc: + - "@apollo/client!ApolloClient#watchFragment:member(1)" --- +import { FunctionDetails, DocBlock, Example } from '../../../shared/ApiDoc'; + Methods of the `InMemoryCache` class (the cache used by almost every instance of [`ApolloClient`](../core/ApolloClient/)) are documented here. > Before reading about individual methods, see [Caching in Apollo Client](../../caching/overview/). @@ -140,6 +144,10 @@ By specifying the ID of another cached object, you can query arbitrary cached da +> **⚠️ Deprecated**: +> Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. +> A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + If `true`, result objects read from the cache will be _canonized_, which means deeply-equal objects will also be `===` (literally the same object), allowing much more efficient comparison of past/present results. The default value is `false`. @@ -591,6 +599,10 @@ A map of any GraphQL variable names and values required by `fragment`. +> **⚠️ Deprecated**: +> Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. +> A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + If `true`, result objects read from the cache will be _canonized_, which means deeply-equal objects will also be `===` (literally the same object), allowing much more efficient comparison of past/present results. The default value is `false`. @@ -760,6 +772,9 @@ writeFragment( options: Cache.WriteFragmentOptions, ): Reference | undefined ``` + + + ## `updateFragment` @@ -1092,7 +1107,7 @@ The full key for the field used internally, including serialized key arguments. -A helper function for reading other fields within the current object. +A helper function for reading other fields on the object passed to the modifier function as the first parameter. diff --git a/docs/source/api/core/ApolloClient.mdx b/docs/source/api/core/ApolloClient.mdx index d4a0467c424..0294973a18e 100644 --- a/docs/source/api/core/ApolloClient.mdx +++ b/docs/source/api/core/ApolloClient.mdx @@ -3,228 +3,66 @@ title: class ApolloClient description: API reference order: 11 api_reference: true +api_doc: + - "@apollo/client!ApolloClient:class" + - "@apollo/client!DefaultOptions:interface" --- +import { InterfaceDetails, FunctionDetails, DocBlock, Example } from '../../../shared/ApiDoc'; + The `ApolloClient` class encapsulates Apollo's core client-side API. It backs all available view-layer integrations (React, iOS, and so on). ## The `ApolloClient` constructor - + -Takes an `ApolloClientOptions` parameter that supports the [fields listed below](#options). +Takes an `ApolloClientOptions` parameter that supports the [fields listed below](#apolloclientoptions). Returns an initialized `ApolloClient` object. #### Example -```js -import { ApolloClient, InMemoryCache } from '@apollo/client'; - -const cache = new InMemoryCache(); - -const client = new ApolloClient({ - // Provide required constructor fields - cache: cache, - uri: 'http://localhost:4000/', - - // Provide some optional constructor fields - name: 'react-web-client', - version: '1.3', - queryDeduplication: false, - defaultOptions: { - watchQuery: { - fetchPolicy: 'cache-and-network', - }, - }, -}); -``` - -#### Options - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +## Types - -
Name /
Type
Description
- -###### `uri` - -`String` - - -The URI of the GraphQL endpoint that Apollo Client will communicate with. - -One of `uri` or `link` is **required**. If you provide both, `link` takes precedence. -
- -###### `link` - -`ApolloLink` - - -You can provide an Apollo Link instance to serve as Apollo Client's network layer. For more information, see [Advanced HTTP networking](../../networking/advanced-http-networking/). - -One of `uri` or `link` is **required**. If you provide both, `link` takes precedence. -
- -###### `cache` - -`ApolloCache` (usually `InMemoryCache`) - - -**Required.** The cache that Apollo Client should use to store query results locally. The recommended cache is `InMemoryCache`, which is provided by the `@apollo/client` package. - -For more information, see [Configuring the cache](../../caching/cache-configuration/). -
- -###### `name` - -`String` - - -A custom name (e.g., `iOS`) that identifies this particular client among your set of clients. Apollo Server and Apollo Studio use this property as part of the [client awareness](/apollo-server/monitoring/metrics/#identifying-distinct-clients) feature. -
- -###### `version` - -`String` - - -A custom version that identifies the current version of this particular client (e.g., `1.2`). Apollo Server and Apollo Studio use this property as part of the [client awareness](/apollo-server/monitoring/metrics/#identifying-distinct-clients) feature. - -This is **not** the version of Apollo Client that you are using, but rather any version string that helps you differentiate between versions of your client. -
+For more information on the `defaultOptions` object, see the [Default Options](#defaultoptions) section below. -###### `ssrMode` - -`Boolean` - - -When using Apollo Client for [server-side rendering](../../performance/server-side-rendering/), set this to `true` so that the [`getDataFromTree` function](../react/ssr/#getdatafromtree) can work effectively. - -The default value is `false`. -
- -###### `ssrForceFetchDelay` - -`Number` - - -The time interval (in milliseconds) before Apollo Client force-fetches queries after a server-side render. - -The default value is `0` (no delay). -
- -###### `connectToDevTools` - -`Boolean` - - -If `true`, the [Apollo Client Devtools](../../development-testing/developer-tooling/#apollo-client-devtools) browser extension can connect to Apollo Client in your production environment. The extension can _always_ connect in a non-production environment. - -The default value is `false`. -
- -###### `queryDeduplication` - -`Boolean` - - -If `false`, Apollo Client sends every created query to the server, even if a _completely_ identical query (identical in terms of query string, variable values, and operationName) is already in flight. - -The default value is `true`. -
- -###### `defaultOptions` - -`Object` - - -Provide this object to set application-wide default values for options you can provide to the `watchQuery`, `query`, and `mutate` functions. See below for an example object. - -See the [example object below](#example-defaultoptions-object). -
- -###### `assumeImmutableResults` - -`Boolean` - +## Functions -If `true`, Apollo Client will assume results read from the cache are never mutated by application code, which enables substantial performance optimizations. + + + + + + + + + + + + + + + + + -The default value is `false`. -
+ + + ##### Example `defaultOptions` object @@ -248,27 +86,3 @@ You can override any default option you specify in this object by providing a different value for the same option in individual function calls. > **Note:** The `useQuery` hook uses Apollo Client's `watchQuery` function. To set `defaultOptions` when using the `useQuery` hook, make sure to set them under the `defaultOptions.watchQuery` property. - -## Functions - - - - - - - - - - - - - - - - - - -## Types - - - diff --git a/docs/source/api/core/ObservableQuery.mdx b/docs/source/api/core/ObservableQuery.mdx index 90d4450dfe6..89c5448be7e 100644 --- a/docs/source/api/core/ObservableQuery.mdx +++ b/docs/source/api/core/ObservableQuery.mdx @@ -1,26 +1,31 @@ --- title: ObservableQuery description: API reference +api_reference: true +api_doc: + - "@apollo/client!ObservableQuery:class" + - "@apollo/client!ApolloQueryResult:interface" + - "@apollo/client!NetworkStatus:enum" --- +import { InterfaceDetails, FunctionDetails, PropertyDetails, EnumDetails } from '../../../shared/ApiDoc'; + ## `ObservableQuery` functions `ApolloClient` Observables extend the Observables implementation provided by [`zen-observable`](https://github.com/zenparsing/zen-observable). Refer to the `zen-observable` documentation for additional context and API options. - - - - - - - - - - - + + + + + + + + + + -## Types - - - +## Types + + diff --git a/docs/source/api/link/apollo-link-remove-typename.mdx b/docs/source/api/link/apollo-link-remove-typename.mdx index 2e190c5f302..201e18b48cd 100644 --- a/docs/source/api/link/apollo-link-remove-typename.mdx +++ b/docs/source/api/link/apollo-link-remove-typename.mdx @@ -6,13 +6,7 @@ minVersion: 3.8.0 ## Overview -When reusing data from a query as an argument to another GraphQL operation, `__typename` fields can cause errors. To avoid this, you can use the `removeTypenameFromVariables` link to automatically remove `__typename` fields from variables in operations. You can import and instantiate it like so: - -```ts -import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename'; - -const removeTypenameLink = removeTypenameFromVariables(); -``` +When reusing data from a query as an argument to another GraphQL operation, `__typename` fields can cause errors. To avoid this, you can use the `removeTypenameFromVariables` link to automatically remove `__typename` fields from variables in operations. ## Remove `__typename` from all variables @@ -60,6 +54,60 @@ await client.mutate({ Without the use of the `removeTypenameFromVariables` link, the server will return an error because `data.dashboard` still contains the `__typename` field. +## Usage + +You can import and instantiate it like so: + +```ts +import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename'; + +const removeTypenameLink = removeTypenameFromVariables(); +``` + +Include `removeTypeNameLink` anywhere in your [link chain](./introduction/#your-first-link-chain) before your [terminating link](./introduction#the-terminating-link) +to remove `__typename` fields from variables for all operations. + +```ts +import { from } from '@apollo/client'; + +const link = from([removeTypenameLink, httpLink]); + +const client = new ApolloClient({ + link, + // ... other options +}); +``` + +If you're using [directional composition](./link/introduction#directional-composition), +for example, to [send a subscription to a websocket connection](../data/subscriptions#3-split-communication-by-operation-recommended), +place `removeTypenameLink` before `splitLink` to remove `__typename` from variables for all operations. + +```ts +import { from, split } from '@apollo/client'; +import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename'; + +const removeTypenameLink = removeTypenameFromVariables(); + +const splitLink = split( + ({ query }) => { + const definition = getMainDefinition(query); + return ( + definition.kind === 'OperationDefinition' && + definition.operation === 'subscription' + ); + }, + wsLink, + httpLink, +); + +const link = from([removeTypenameLink, splitLink]); + +const client = new ApolloClient({ + link, + // ... other options +}); +``` + ## Keep `__typename` in JSON scalars Sometimes, you may need to retain the `__typename` field from a query's response—for example, in the case of [JSON scalar](https://github.com/taion/graphql-type-json) input fields. @@ -194,4 +242,3 @@ Determines which input types should retain `__typename`. This maps the input typ - diff --git a/docs/source/api/link/introduction.mdx b/docs/source/api/link/introduction.mdx index 200d8dc7aed..80d390c40ca 100644 --- a/docs/source/api/link/introduction.mdx +++ b/docs/source/api/link/introduction.mdx @@ -335,10 +335,23 @@ This style of link also composes well for customization using a function: import { ApolloLink } from '@apollo/client'; const reportErrors = (errorCallback) => new ApolloLink((operation, forward) => { - const observable = forward(operation); - // errors will be sent to the errorCallback - observable.subscribe({ error: errorCallback }) - return observable; + return new Observable((observer) => { + const observable = forward(operation); + const subscription = observable.subscribe({ + next(value) { + observer.next(value); + }, + error(networkError) { + errorCallback({ networkError, operation }); + observer.error(networkError); + }, + complete() { + observer.complete(); + }, + }); + + return () => subscription.unsubscribe(); + }); }); const link = reportErrors(console.error); diff --git a/docs/source/api/link/persisted-queries.mdx b/docs/source/api/link/persisted-queries.mdx index e2b4a35da86..4d71bfbf25d 100644 --- a/docs/source/api/link/persisted-queries.mdx +++ b/docs/source/api/link/persisted-queries.mdx @@ -31,21 +31,21 @@ Using persisted queries for safelisting has the following requirements: - Apollo Client Web (v3.7.0+) - The [`@apollo/generate-persisted-query-manifest` package](https://www.npmjs.com/package/@apollo/generate-persisted-query-manifest) - The [`@apollo/persisted-query-lists` package](https://www.npmjs.com/package/@apollo/persisted-query-lists) -- [Apollo Router](/router) (v1.25.0+) +- [GraphOS Router](/router) (v1.25.0+) - [GraphOS Enterprise plan](/graphos/enterprise/) -You can use APQ with the following versions of Apollo Client Web, Apollo Server, and Apollo Router: +You can use APQ with the following versions of Apollo Client Web, Apollo Server, and Apollo Router Core: - Apollo Client Web (v3.2.0+) - [Apollo Server](/apollo-server/) (v1.0.0+) -- [Apollo Router](/router) (v0.1.0+) +- [Apollo Router Core](/router) (v0.1.0+) -> **Note:** You can use _either_ Apollo Server _or_ Apollo Router for APQs. They don't need to be used together. +> **Note:** You can use _either_ Apollo Server _or_ Apollo Router Core for APQs. They don't need to be used together. ## 1. Generate operation manifests > **This step is only required for persisted queries, not APQ.** -An operation manifest acts as a safelist the [Apollo Router](/router/) can check incoming requests against. +An operation manifest acts as a safelist the [GraphOS Router](/router/) can check incoming requests against. You can generate the manifest using the [`@apollo/generate-persisted-query-manifest`](https://www.npmjs.com/package/@apollo/generate-persisted-query-manifest) package: 1. Install the [`@apollo/generate-persisted-query-manifest`](https://www.npmjs.com/package/@apollo/generate-persisted-query-manifest) package as a dev dependency: diff --git a/docs/source/api/react/components.mdx b/docs/source/api/react/components.mdx index 629d1475c8d..9aeca723eda 100644 --- a/docs/source/api/react/components.mdx +++ b/docs/source/api/react/components.mdx @@ -1,14 +1,16 @@ --- title: Components description: Deprecated React Apollo render prop component API +api_doc: + - "@apollo/client!QueryFunctionOptions:interface" + - "@apollo/client!QueryResult:interface" + - "@apollo/client!MutationFunctionOptions:interface" + - "@apollo/client!MutationResult:interface" + - "@apollo/client!SubscriptionComponentOptions:interface" + - "@apollo/client!SubscriptionResult:interface" --- -import QueryOptions3 from '../../../shared/query-options.mdx'; -import QueryResult3 from '../../../shared/query-result.mdx'; -import MutationOptions3 from '../../../shared/mutation-options.mdx'; -import MutationResult3 from '../../../shared/mutation-result.mdx'; -import SubscriptionOptions3 from '../../../shared/subscription-options.mdx'; -import SubscriptionResult3 from '../../../shared/subscription-result.mdx'; +import { PropertySignatureTable } from '../../../shared/ApiDoc'; > **Note:** Official support for React Apollo render prop components ended in March 2020. This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. @@ -28,25 +30,25 @@ You then import the library's symbols from `@apollo/client/react/components`. The `Query` component accepts the following props. `query` is **required**. - + ### Render prop function The render prop function that you pass to the `children` prop of `Query` is called with an object (`QueryResult`) that has the following properties. This object contains your query result, plus some helpful functions for refetching, dynamic polling, and pagination. - + ## `Mutation` The Mutation component accepts the following props. Only `mutation` is **required**. - + ### Render prop function The render prop function that you pass to the `children` prop of `Mutation` is called with the `mutate` function and an object with the mutation result. The `mutate` function is how you trigger the mutation from your UI. The object contains your mutation result, plus loading and error state. - + ## `Subscription` @@ -54,10 +56,10 @@ The render prop function that you pass to the `children` prop of `Mutation` is c The Subscription component accepts the following props. Only `subscription` is **required**. - + ### Render prop function -The render prop function that you pass to the `children` prop of `Subscription` is called with an object that has the following properties. + diff --git a/docs/source/api/react/hooks-experimental.mdx b/docs/source/api/react/hooks-experimental.mdx index 99212fa7c30..2557f4e89a9 100644 --- a/docs/source/api/react/hooks-experimental.mdx +++ b/docs/source/api/react/hooks-experimental.mdx @@ -3,4 +3,4 @@ title: Hooks (experimental) description: Apollo Client experimental react hooks API reference --- -The latest minor version of Apollo Client (`3.8`) has no experimental hooks. Please see the [Hooks page](./hooks) for a list of available stable React hooks. +The latest minor version of Apollo Client has no experimental hooks. Please see the [Hooks page](./hooks) for a list of available stable React hooks. diff --git a/docs/source/api/react/hooks.mdx b/docs/source/api/react/hooks.mdx index 9466e50f24d..22358afd152 100644 --- a/docs/source/api/react/hooks.mdx +++ b/docs/source/api/react/hooks.mdx @@ -1,25 +1,27 @@ --- title: Hooks description: Apollo Client react hooks API reference +minVersion: 3.0.0 +api_doc: + - "@apollo/client!SuspenseQueryHookOptions:interface" + - "@apollo/client!useQuery:function(1)" + - "@apollo/client!useLazyQuery:function(1)" + - "@apollo/client!useMutation:function(1)" + - "@apollo/client!useSubscription:function(1)" + - "@apollo/client!useApolloClient:function(1)" + - "@apollo/client!useReactiveVar:function(1)" + - "@apollo/client!useLoadableQuery:function(5)" + - "@apollo/client!useQueryRefHandlers:function(1)" --- -import QueryOptions3 from '../../../shared/query-options.mdx'; -import QueryResult3 from '../../../shared/query-result.mdx'; -import MutationOptions3 from '../../../shared/mutation-options.mdx'; -import MutationResult3 from '../../../shared/mutation-result.mdx'; -import SubscriptionOptions3 from '../../../shared/subscription-options.mdx'; -import SubscriptionResult3 from '../../../shared/subscription-result.mdx'; import UseFragmentOptions from '../../../shared/useFragment-options.mdx'; import UseFragmentResult from '../../../shared/useFragment-result.mdx'; -import UseSuspenseQueryOptions from '../../../shared/useSuspenseQuery-options.mdx'; import UseBackgroundQueryOptions from '../../../shared/useBackgroundQuery-options.mdx'; import UseSuspenseQueryResult from '../../../shared/useSuspenseQuery-result.mdx'; import UseBackgroundQueryResult from '../../../shared/useBackgroundQuery-result.mdx'; import UseReadQueryResult from '../../../shared/useReadQuery-result.mdx'; - -## Installation - -Apollo Client >= 3 includes React hooks functionality out of the box. You don't need to install any additional packages. +import { FunctionDetails, PropertySignatureTable, ManualTuple, InterfaceDetails } from '../../../shared/ApiDoc'; +import { UseLoadableQueryResult } from '../../../shared/Overrides/UseLoadableQueryResult' ## The `ApolloProvider` component @@ -69,350 +71,93 @@ function WithApolloClient() { } ``` -## `useQuery` - -### Example - -```jsx -import { gql, useQuery } from '@apollo/client'; - -const GET_GREETING = gql` - query GetGreeting($language: String!) { - greeting(language: $language) { - message - } - } -`; - -function Hello() { - const { loading, error, data } = useQuery(GET_GREETING, { - variables: { language: 'english' }, - }); - if (loading) return

Loading ...

; - return

Hello {data.greeting.message}!

; -} -``` - -> Refer to the [Queries](../../data/queries/) section for a more in-depth overview of `useQuery`. - -### Signature - -```ts -function useQuery( - query: DocumentNode, - options?: QueryHookOptions, -): QueryResult {} -``` - -### Params - -#### `query` - -| Param | Type | Description | -| ------- | ------------ | ------------------------------------------------------------- | -| `query` | DocumentNode | A GraphQL query document parsed into an AST by `gql`. | - -#### `options` + - - -### Result - - - -## `useLazyQuery` - -### Example - -```jsx -import { gql, useLazyQuery } from "@apollo/client"; - -const GET_GREETING = gql` - query GetGreeting($language: String!) { - greeting(language: $language) { - message - } - } -`; - -function Hello() { - const [loadGreeting, { called, loading, data }] = useLazyQuery( - GET_GREETING, - { variables: { language: "english" } } - ); - if (called && loading) return

Loading ...

- if (!called) { - return - } - return

Hello {data.greeting.message}!

; -} -``` + +

+[execute: LazyQueryExecFunction<TData, TVariables>, result: QueryResult<TData, TVariables>]
+
-> Refer to the [Queries](../../data/queries/) section for a more in-depth overview of `useLazyQuery`. +A tuple of two values: -### Signature - -```ts -function useLazyQuery( - query: DocumentNode, - options?: LazyQueryHookOptions, -): [ - (options?: LazyQueryHookOptions) => Promise>, - LazyQueryResult -] {} -``` - -### Params - -#### `query` - -| Param | Type | Description | -| ------- | ------------ | ------------------------------------------------------------- | -| `query` | DocumentNode | A GraphQL query document parsed into an AST by `gql`. | - -#### `options` - - - -### Result tuple - -**Execute function (first tuple item)** - -| Param | Type | Description | -| ---------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| Execute function | `(options?: LazyQueryHookOptions) => Promise>` | Function that can be triggered to execute the suspended query. After being called, `useLazyQuery` behaves just like `useQuery`. The `useLazyQuery` function returns a promise that fulfills with a query result when the query succeeds or fails. | - -**`LazyQueryResult` object (second tuple item)** - - - -## `useMutation` - -### Example - -```jsx -import { gql, useMutation } from '@apollo/client'; - -const ADD_TODO = gql` - mutation AddTodo($type: String!) { - addTodo(type: $type) { - id - type - } +) => Promise>", + description: "Function that can be triggered to execute the suspended query. After being called, `useLazyQuery` behaves just like `useQuery`. The `useLazyQuery` function returns a promise that fulfills with a query result when the query succeeds or fails." + }, + { + name: "result", + type: "QueryResult", + description: "The result of the query. See the `useQuery` hook for more details.", + canonicalReference: "@apollo/client!QueryResult:interface" } -`; - -function AddTodo() { - let input; - const [addTodo, { data }] = useMutation(ADD_TODO); - - return ( +]}/> +} +/> + + -
{ - e.preventDefault(); - addTodo({ variables: { type: input.value } }); - input.value = ''; - }} - > - { - input = node; - }} - /> - -
+
+        
+        {`[
+  mutate: (options?: MutationFunctionOptions) => Promise>,
+  result: MutationResult
+]`}
+        
+      
+ A tuple of two values: + + ) => Promise>`, + description:
+A function to trigger the mutation from your UI. You can optionally pass this function any of the following options: + +
    +
  • awaitRefetchQueries
  • +
  • context
  • +
  • fetchPolicy
  • +
  • onCompleted
  • +
  • onError
  • +
  • optimisticResponse
  • +
  • refetchQueries
  • +
  • onQueryUpdated
  • +
  • update
  • +
  • variables
  • +
  • client
  • +
+ +Any option you pass here overrides any existing value for that option that you passed to useMutation. + +The mutate function returns a promise that fulfills with your mutation result. +
, + }, + { + name: "result", + type: "MutationResult", + description: "The result of the mutation.", + canonicalReference: "@apollo/client!MutationResult:interface", + }, + ]} + /> - ); -} -``` - -> Refer to the [Mutations](../../data/mutations/) section for a more in-depth overview of `useMutation`. - -### Signature - -```ts -function useMutation( - mutation: DocumentNode, - options?: MutationHookOptions, -): MutationTuple {} -``` - -### Params - -#### `mutation` - -| Param | Type | Description | -| ---------- | ------------ | ---------------------------------------------------------------- | -| `mutation` | DocumentNode | A GraphQL mutation document parsed into an AST by `gql`. | - -#### `options` - - - -### `MutationTuple` result tuple - - - -## `useSubscription` - -### Example - -```jsx -const COMMENTS_SUBSCRIPTION = gql` - subscription OnCommentAdded($repoFullName: String!) { - commentAdded(repoFullName: $repoFullName) { - id - content - } } -`; - -function DontReadTheComments({ repoFullName }) { - const { - data: { commentAdded }, - loading, - } = useSubscription(COMMENTS_SUBSCRIPTION, { variables: { repoFullName } }); - return

New comment: {!loading && commentAdded.content}

; -} -``` - -> Refer to the [Subscriptions](../../data/subscriptions/) section for a more in-depth overview of `useSubscription`. - -#### Subscriptions and React 18 Automatic Batching - -With React 18's [automatic batching](https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching), multiple state updates may be grouped into a single re-render for better performance. - -If your subscription API sends multiple messages at the same time or in very fast succession (within fractions of a millisecond), it is likely that only the last message received in that narrow time frame will result in a re-render. +/> -Consider the following component: + -```jsx -export function Subscriptions() { - const { data, error, loading } = useSubscription(query); - const [accumulatedData, setAccumulatedData] = useState([]); - - useEffect(() => { - setAccumulatedData((prev) => [...prev, data]); - }, [data]); - - return ( - <> - {loading &&

Loading...

} - {JSON.stringify(accumulatedData, undefined, 2)} - - ); -} -``` - -If your subscription back-end emits two messages with the same timestamp, only the last message received by Apollo Client will be rendered. This is because React 18 will batch these two state updates into a single re-render. - -Since the component above is using `useEffect` to push `data` into a piece of local state on each `Subscriptions` re-render, the first message will never be added to the `accumulatedData` array since its render was skipped. + -Instead of using `useEffect` here, we can re-write this component to use the `onData` callback function accepted in `useSubscription`'s `options` object: - -```jsx -export function Subscriptions() { - const [accumulatedData, setAccumulatedData] = useState([]); - const { data, error, loading } = useSubscription( - query, - { - onData({ data }) { - setAccumulatedData((prev) => [...prev, data]) - } - } - ); - - return ( - <> - {loading &&

Loading...

} - {JSON.stringify(accumulatedData, undefined, 2)} - - ); -} -``` - -> ⚠️ **Note:** The `useSubscription` option `onData` is available in Apollo Client >= 3.7. In previous versions, the equivalent option is named `onSubscriptionData`. - -Now, the first message will be added to the `accumulatedData` array since `onData` is called _before_ the component re-renders. React 18 automatic batching is still in effect and results in a single re-render, but with `onData` we can guarantee each message received after the component mounts is added to `accumulatedData`. - -### Signature - -```ts -function useSubscription( - subscription: DocumentNode, - options?: SubscriptionHookOptions, -): { - variables: TVariables; - loading: boolean; - data?: TData; - error?: ApolloError; -} {} -``` - -### Params - -#### `subscription` - -| Param | Type | Description | -| -------------- | ------------ | -------------------------------------------------------------------- | -| `subscription` | DocumentNode | A GraphQL subscription document parsed into an AST by `gql`. | - -#### `options` - - - -### Result - - - -## `useApolloClient` - -### Example - -```jsx -import { useApolloClient } from '@apollo/client'; - -function SomeComponent() { - const client = useApolloClient(); - // `client` is now set to the `ApolloClient` instance being used by the - // application (that was configured using something like `ApolloProvider`) -} -``` - -### Signature - -```ts -function useApolloClient(): ApolloClient {} -``` - -### Result - -| Param | Type | Description | -| ---------------------- | -------------------------- | ---------------------------------------------------------- | -| Apollo Client instance | ApolloClient<object> | The `ApolloClient` instance being used by the application. | - - -## `useReactiveVar` - -Reads the value of a [reactive variable](../../local-state/reactive-variables/) and re-renders the containing component whenever that variable's value changes. This enables a reactive variable to trigger changes _without_ relying on the `useQuery` hook. - -### Example - -```jsx -import { makeVar, useReactiveVar } from "@apollo/client"; -export const cartItemsVar = makeVar([]); - -export function Cart() { - const cartItems = useReactiveVar(cartItemsVar); - // ... -``` - -### Signature - -```tsx -function useReactiveVar(rv: ReactiveVar): T {} -``` + @@ -443,7 +188,6 @@ function useFragment< optimistic?: boolean; variables?: TVars; returnPartialData?: boolean; - canonizeResults?: boolean; }): UseFragmentResult {} ``` @@ -521,7 +265,11 @@ function useSuspenseQuery( Instead of passing a `SuspenseQueryHookOptions` object into the hook, you can also pass a [`skipToken`](#skiptoken) to prevent the `useSuspenseQuery` hook from executing the query or suspending. - + ### Result @@ -595,7 +343,7 @@ function useBackgroundQuery( ): [ // Will return `undefined` here if no query has been executed yet and the query // is currently skipped using `skipToken` or { skip: true } - QueryReference | undefined, + QueryRef | undefined, { fetchMore: FetchMoreFunction; refetch: RefetchFunction; @@ -640,7 +388,7 @@ See the [example](#example-10) in the `useBackgroundQuery` section above. ```ts function useReadQuery( - queryRef: QueryReference + queryRef: QueryRef ): { data: TData; networkStatus: NetworkStatus; @@ -654,12 +402,20 @@ function useReadQuery( | Param | Type | Description | | ------- | ------------ | ------------------------------------------------------------- | -| `queryRef` | QueryReference | The [`queryRef`](#queryref) that was generated via `useBackgroundQuery`. | +| `queryRef` | QueryRef | The [`queryRef`](#queryref) that was generated via `useBackgroundQuery`. | ### Result +} +/> + + + ## `skipToken` diff --git a/docs/source/api/react/preloading.mdx b/docs/source/api/react/preloading.mdx new file mode 100644 index 00000000000..644fe13d122 --- /dev/null +++ b/docs/source/api/react/preloading.mdx @@ -0,0 +1,11 @@ +--- +title: Preloading +description: Apollo Client preloading API reference +minVersion: 3.9.0 +api_doc: + - "@apollo/client!createQueryPreloader:function(1)" +--- + +import { FunctionDetails } from '../../../shared/ApiDoc'; + + diff --git a/docs/source/caching/advanced-topics.mdx b/docs/source/caching/advanced-topics.mdx index da9e1cfeeca..4be10cd6a5f 100644 --- a/docs/source/caching/advanced-topics.mdx +++ b/docs/source/caching/advanced-topics.mdx @@ -2,7 +2,7 @@ title: Advanced topics on caching in Apollo Client --- -This article describes special cases and considerations when using the Apollo Client cache. +This article describes special cases and considerations when using the [Apollo Client cache](./overview). ## Bypassing the cache @@ -22,7 +22,11 @@ You can persist and rehydrate the `InMemoryCache` from a storage provider like ` To get started, pass your cache and a storage provider to `persistCache`. By default, the contents of your cache are immediately restored asynchronously, and they're persisted on every write to the cache with a short configurable debounce interval. -> **Note:** The `persistCache` method is async and returns a `Promise`. + + +The `persistCache` method is async and returns a `Promise`. + + ```js import AsyncStorage from '@react-native-async-storage/async-storage'; @@ -60,7 +64,7 @@ function Profile() { } ``` -> To reset the cache _without_ refetching active queries, use `client.clearStore()` instead of `client.resetStore()`. +To reset the cache _without_ refetching active queries, use `client.clearStore()` instead of `client.resetStore()`. ### Responding to cache resets @@ -97,7 +101,7 @@ function Foo (){ const client = useApolloClient(); useEffect(() => { - const unsubscribe = client.onResetStore(() => + const unsubscribe = client.onResetStore(() => new Promise(()=>setReset(reset + 1)) ); @@ -112,7 +116,7 @@ function Foo (){ export default Foo; ``` -## TypePolicy inheritence +## TypePolicy inheritance JavaScript developers will be familiar with the idea of [inheritance](https://en.m.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) from the `extends` clause of `class` declarations, or possibly from dealing with prototype chains created by `Object.create`. @@ -174,7 +178,11 @@ In these cases, you can provide a `refetchQueries` option to the `useMutation` h For details, see [Refetching queries](../data/mutations/#refetching-queries). -> Note that although `refetchQueries` can be faster to implement than an `update` function, it also requires additional network requests that are usually undesirable. For more information, see [this blog post](https://www.apollographql.com/blog/when-to-use-refetch-queries-in-apollo-client/). + + +Although `refetchQueries` can be faster to implement than an `update` function, it also requires additional network requests that are usually undesirable. For more information, see [this blog post](https://www.apollographql.com/blog/when-to-use-refetch-queries/). + + ## Cache redirects @@ -233,23 +241,61 @@ This `read` function uses the `toReference` helper utility to generate and retur Now whenever a query includes the `book` field, the `read` function above executes and returns a reference to a `Book` object. Apollo Client uses this reference to look up the object in its cache and return it if it's present. If it _isn't_ present, Apollo Client knows it needs to execute the query over the network. -> ⚠️ **Note:** To avoid a network request, _all_ of a query's requested fields must already be present in the cache. If the detail view's query fetches _any_ `Book` field that the list view's query _didn't_, Apollo Client considers the cache hit to be incomplete, and it executes the full query over the network. + + +To avoid a network request, _all_ of a query's requested fields must already be present in the cache. If the detail view's query fetches _any_ `Book` field that the list view's query _didn't_, Apollo Client considers the cache hit to be incomplete, and it executes the full query over the network. + + ## Pagination utilities +Pagination is a best practice in GraphQL [for several reasons](../pagination/overview). Apollo Client enables fetching and caching paginated results using the [Core pagination API](../pagination/core-api). The API includes a few important utilities, including the [`fetchMore`](../pagination/core-api/#the-fetchmore-function) function and the `@connection` directive. + ### Incremental loading: `fetchMore` -You can use the `fetchMore` function to update a query's cached result with data returned by a _followup_ query. Most often, `fetchMore` is used to handle infinite-scroll pagination and other situations where you're loading _more_ data when you already have _some_. +You can use the `fetchMore` function to update a query's cached result with data returned by a _follow-up_ query. Most often, `fetchMore` is used to handle infinite-scroll pagination and other situations where you're loading more data when you already have some. For details, see [The `fetchMore` function](../pagination/core-api/#the-fetchmore-function). ### The `@connection` directive -Fundamentally, paginated queries are the same as any other query with the exception that calls to `fetchMore` update the same cache key. Because these queries are cached by both the initial query and their parameters, a problem arises when later retrieving or updating paginated queries in the cache. We don't care about pagination arguments such as limits, offsets, or cursors outside of the need to `fetchMore`, nor do we want to provide them simply for accessing cached data. +The `@connection` directive solves the problem of multiple copies of the same field in the cache. This can happen with paginated queries because the `fetchMore` function sends follow-up queries to fetch additional pages of results using arguments like [`offset`](../pagination/offset-based/) and [`limit`](../pagination/offset-based/). These arguments inadvertently fragment data from different pagination requests across the cache. + +The `@connection` directive lets you unify paginated results by specifying a custom, stable cache key for a field. It also lets you _intentionally_ separate paginated results in the cache by fields that you specify. + + + +Starting in Apollo Client v3, setting the [`keyArgs` field policy](../pagination/key-args/#setting-keyargs) is the most straightforward way to resolve fragmented pagination results in the cache. For example, setting [`keyArgs` to `false`](../pagination/key-args/#supported-values-for-keyargs) indicates that no arguments should be included in cache keys, causing all pagination results to be cached together. Additionally, you only have to set your `keyArgs` configuration once, rather than using `@connection` in multiple queries. Refer to the [usage instructions](#connection-directive-usage) below to compare `@connection` and `keyArgs` usage. + +The `@connection` directive is useful when you want to store distinct data in the cache on a query-by-query, field-by-field basis. See the [advanced usage instructions](#advanced-connection-directive-usage) for more details. + + + +#### `@connection` directive usage + + + +For the standard `@connection` directive usage described in this section, it's best to configure a [`keyArgs` field policy](../pagination/key-args/#setting-keyargs). For example, you can use the following [`keyArgs`](../pagination/key-args/#setting-keyargs) configuration for the same effect as the `@connection` example below. + +```js +const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + feed: { + keyArgs: ["type"] + } + } + } + } +}) +``` + +With this centralized `keyArg`s configuration, you don't need to include the `@connection` directive in your queries because the `type` argument is adequate for keeping feeds of different types separate in the cache. For an example of storing distinct data on a query-by-query basis, see the [advanced usage instructions](#advanced-connection-directive-usage). -To solve this, you can use the `@connection` directive to specify a custom cache key for results. A connection allows us to set the cache key for a field and to filter which arguments actually alter the query. + -To use the `@connection` directive, add it to the segment of the query you want a custom store key for and provide the `key` parameter to specify the store key. In addition to the `key` parameter, you can also include the optional `filter` parameter, which takes an array of query argument names to include in the generated custom store key. +To use the `@connection` directive, add it to the field you want a custom cache key for. The directive requires a `key` parameter to specify the custom cache key. You can optionally include the `filter` parameter, which takes an array of query argument names to include in the generated custom cache key. ```js const query = gql` @@ -261,9 +307,9 @@ const query = gql` ` ``` -With the above query, even with multiple `fetchMore`s, the results of each feed update will always result in the `feed` key in the store being updated with the latest accumulated values. In this example, we also use the `@connection` directive's optional `filter` argument to include the `type` query argument in the store key, which results in multiple store values that accumulate queries from each type of feed. +With the above query, even when multiple `fetchMore`s queries are performed, each feed update always results in an update to the cache's `feed` key with the latest accumulated values. The example also uses the `@connection` directive's optional `filter` argument to include the `type` query argument in the cache key. This creates multiple cache values that accumulate queries from each type of feed. -Now that we have a stable store key, we can easily use `writeQuery` to perform a store update, in this case clearing out the feed. +With a stable cache key, you can use [`writeQuery`](./cache-interaction/#writequery) to perform a cache update that clears out the feed. ```js client.writeQuery({ @@ -283,4 +329,24 @@ client.writeQuery({ }); ``` -Note that because we are only using the `type` argument in the store key, we don't have to provide `offset` or `limit`. + + +Because this example uses the `type` argument in the cache key, it doesn't need to provide `offset` or `limit` arguments. + + + +#### Advanced `@connection` directive usage + +The `@connection` directive is useful when using the same field in multiple queries, with no distinguishing arguments (for example, `type`) that `keyArgs` can use, and you want to keep that field's data separate in the cache. + +For example, Apollo's [Spotify showcase](https://github.com/apollographql/spotify-showcase) uses `@connection` to independently cache lists of playlists. One list is in the left sidebar, where you navigate between playlists. The other appears when you right-click a song to add it to a playlist. + + +Separately cached playlists in Apollo's Spotify Showcase + +Without caching the playlists separately, loading the next page of data from one list affects the other, negatively impacting the UX. + +For code examples, see: +- [The type policy](https://github.com/apollographql/spotify-showcase/blob/185f7b8a155209e9a099490dbc5d1e3bfba4c32f/client/src/apollo/client.ts#L105-L108) +- [Playlist sidebar query](https://github.com/apollographql/spotify-showcase/blob/185f7b8a155209e9a099490dbc5d1e3bfba4c32f/client/src/components/LoggedInLayout.tsx#L75) +- [Add to playlist menu](https://github.com/apollographql/spotify-showcase/blob/185f7b8a155209e9a099490dbc5d1e3bfba4c32f/client/src/components/ContextMenuAction/AddToPlaylist.tsx#L17) diff --git a/docs/source/caching/cache-configuration.mdx b/docs/source/caching/cache-configuration.mdx index 830560c2d4c..d114b9f1e11 100644 --- a/docs/source/caching/cache-configuration.mdx +++ b/docs/source/caching/cache-configuration.mdx @@ -247,7 +247,7 @@ const cache = new InMemoryCache({ fields: { name: { read(name = "UNKNOWN NAME") { - return name.toUpperCase();; + return name.toUpperCase(); } }, }, diff --git a/docs/source/caching/cache-interaction.mdx b/docs/source/caching/cache-interaction.mdx index 240c891e437..4331a5da14f 100644 --- a/docs/source/caching/cache-interaction.mdx +++ b/docs/source/caching/cache-interaction.mdx @@ -1,7 +1,11 @@ --- title: Reading and writing data to the cache +api_doc: + - "@apollo/client!ApolloClient#watchFragment:member(1)" --- +import { DocBlock } from '../../shared/ApiDoc'; + You can read and write data directly to the Apollo Client cache, _without_ communicating with your GraphQL server. You can interact with data that you previously fetched from your server, _and_ with data that's only available [locally](../local-state/local-state-management/). Apollo Client supports multiple strategies for interacting with cached data: @@ -12,7 +16,9 @@ Apollo Client supports multiple strategies for interacting with cached data: | [Using GraphQL fragments](#using-graphql-fragments) | `readFragment` / `writeFragment` / `updateFragment` / `useFragment` | Access the fields of any cached object without composing an entire query to reach that object. | | [Directly modifying cached fields](#using-cachemodify) | `cache.modify` | Manipulate cached data without using GraphQL at all. | - You can use whichever combination of strategies and methods are most helpful for your use case. +You can use whichever combination of strategies and methods are most helpful for your use case. + +Bear in mind the difference between fields that contain references to other objects in the cache and fields that contain literal values. References are objects that contain a `__ref` field—see the [example](./overview/#example) in the caching overview. Modifying a reference will not change the values contained in the object to which the reference points. So avoid updating an object from something like `{__ref: '5'}` to `{__ref: '5', completed: true}`. > All code samples below assume that you have initialized an instance of `ApolloClient` and that you have imported the `gql` function from `@apollo/client`. If you haven't, [get started](../get-started). > @@ -209,6 +215,14 @@ client.writeFragment({ All subscribers to the Apollo Client cache (including all active queries) see this change and update your application's UI accordingly. + + +### `watchFragment` + + + + + ### `useFragment` @@ -338,7 +352,7 @@ cache.modify({ When you define a modifier function for a field that contains a scalar, an enum, or a list of these base types, the modifier function is passed the exact existing value for the field. For example, if you define a modifier function for an object's `quantity` field that has current value `5`, your modifier function is passed the value `5`. -**However**, when you define a modifier function for a field that contains an object type or a list of objects, those objects are represented as **references**. Each reference points to its corresponding object in the cache by its cache ID. If you return a _different_ reference in your modifier function, you change _which_ other cached object is contained in this field. You _don't_ modify the original cached object's data. +**However**, when you define a modifier function for a field that contains an object type or a list of objects, those objects are represented as **references**. Each reference points to its corresponding object in the cache by its cache ID. If you return a _different_ reference in your modifier function, you change _which_ other cached object is contained in this field. You _don't_ modify the original cached object's data. Additionally, modifying a field (or adding a new one) in a reference will only take effect for the location you're modifying. ### Modifier function utilities diff --git a/docs/source/caching/garbage-collection.mdx b/docs/source/caching/garbage-collection.mdx index b77643a255a..80fc7e768a8 100644 --- a/docs/source/caching/garbage-collection.mdx +++ b/docs/source/caching/garbage-collection.mdx @@ -33,6 +33,10 @@ cache.gc({ }) ``` +> **⚠️ Deprecation warning for `canonizeResults**: +> Using `canonizeResults` can result in memory leaks so we generally do not recommend using this option anymore. +> A future version of Apollo Client will contain a similar feature without the risk of memory leaks. + These additional `cache.gc` options can be useful for investigating memory usage patterns or leaks. Before taking heap snapshots or recording allocation timelines, it's a good idea to force _JavaScript_ garbage collection using your browser's devtools, to ensure memory released by the cache has been fully collected and returned to the heap. ### Configuring garbage collection diff --git a/docs/source/caching/memory-management.mdx b/docs/source/caching/memory-management.mdx new file mode 100644 index 00000000000..d5b213fed59 --- /dev/null +++ b/docs/source/caching/memory-management.mdx @@ -0,0 +1,127 @@ +--- +title: Memory management +api_doc: + - "@apollo/client!CacheSizes:interface" + - "@apollo/client!ApolloClient:class" +subtitle: Learn how to choose and set custom cache sizes +description: Learn how to choose and set custom cache sizes with Apollo Client. +minVersion: 3.9.0 +--- + +import { Remarks, PropertySignatureTable, Example } from '../../shared/ApiDoc'; + +## Cache Sizes + +For better performance, Apollo Client caches (or, in other words, memoizes) many +internally calculated values. +In most cases, these values are cached in [weak caches](https://en.wikipedia.org/wiki/Weak_reference), which means that if the +source object is garbage-collected, the cached value will be garbage-collected, +too. + +These caches are also Least Recently Used (LRU) caches, meaning that if the cache is full, +the least recently used value will be garbage-collected. + +Depending on your application, you might want to tweak the cache size to fit your +needs. + +You can set your cache size [before (recommended)](#setting-cache-sizes-before-loading-the-apollo-client-library) or [after](#adjusting-cache-sizes-after-loading-the-apollo-client-library) loading the Apollo Client library. + +### Setting cache sizes before loading the Apollo Client library + +Setting cache sizes before loading the Apollo Client library is recommended because some caches are already initialized when the library is loaded. Changed cache sizes only +affect caches created after the fact, so you'd have to write additional runtime code to recreate these caches after changing their size. + + ```ts +import type { CacheSizes } from '@apollo/client/utilities'; + + globalThis[Symbol.for("apollo.cacheSize")] = { + parser: 100, + "fragmentRegistry.lookup": 500 + } satisfies Partial + ``` + +### Adjusting cache sizes after loading the Apollo Client library + +You can also adjust cache sizes after loading the library. + +```js +import { cacheSizes } from '@apollo/client/utilities'; +import { print } from '@apollo/client' + +cacheSizes.print = 100; +// cache sizes changed this way will only take effect for caches +// created after the cache size has been changed, so we need to +// reset the cache for it to be effective + +print.reset(); +``` + +### Choosing appropriate cache sizes + + + +To choose good sizes for our memoization caches, you need to know what they +use as source values, and have a general understanding of the data flow inside of +Apollo Client. + +For most memoized values, the source value is a parsed GraphQL document— +a `DocumentNode`. There are two types: + +* **User-supplied `DocumentNode`s** are created + by the user, for example by using the `gql` template literal tag. + This is the `QUERY`, `MUTATION`, or `SUBSCRIPTION` argument passed + into a [`useQuery` hook](../data/queries/#usequery-api) or as the `query` option to `client.query`. +* **Transformed `DocumentNode`s** are derived from + user-supplied `DocumentNode`s, for example, by applying [`DocumentTransform`s](../data/document-transforms/) to them. + +As a rule of thumb, you should set the cache sizes for caches using a transformed +`DocumentNode` at least to the same size as for caches using a user-supplied +`DocumentNode`. If your application uses a custom `DocumentTransform` that does +not always transform the same input to the same output, you should set the cache +size for caches using a Transformed `DocumentNode` to a higher value than for +caches using a user-supplied `DocumentNode`. + +By default, Apollo Client uses a base value of 1000 cached objects for caches using +user-supplied `DocumentNode` instances, and scales other cache sizes relative +to that. For example, the default base value of 1000 for user-provided `DocumentNode`s would scale to 2000, 4000, etc. for transformed `DocumentNode`s, depending on the transformation performed. + +This base value should be plenty for most applications, but you can tweak them if you have different requirements. + +#### Measuring cache usage + +Since estimating appropriate cache sizes for your application can be hard, Apollo Client +exposes an API for cache usage measurement.
+This way, you can click around in your application and then take a look at the +actual usage of the memoizing caches. + +Keep in mind that this API is primarily meant for usage with the Apollo DevTools +(an integration is coming soon), and the API may change at any +point in time.
+It is also only included in development builds, not in production builds. + + + +The cache usage API is only meant for manual measurements. Don't rely on it in production code or tests. + + + + + + + +### Cache options + + diff --git a/docs/source/config.json b/docs/source/config.json index 6862f2e7836..5e85cecba5f 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -28,6 +28,7 @@ "Reading and writing": "/caching/cache-interaction", "Garbage collection and eviction": "/caching/garbage-collection", "Customizing field behavior": "/caching/cache-field-behavior", + "Memory Management": "/caching/memory-management", "Advanced topics": "/caching/advanced-topics" }, "Pagination": { @@ -48,6 +49,7 @@ "Developer tools": "/development-testing/developer-tooling", "Using TypeScript": "/development-testing/static-typing", "Testing React components": "/development-testing/testing", + "Schema-driven testing": "/development-testing/schema-driven-testing", "Mocking schema capabilities": "/development-testing/client-schema-mocking", "Reducing bundle size": "/development-testing/reducing-bundle-size" }, @@ -79,6 +81,7 @@ }, "React": { "Hooks": "/api/react/hooks", + "Preloading": "/api/react/preloading", "Testing": "/api/react/testing", "SSR": "/api/react/ssr", "Components (deprecated)": "/api/react/components", diff --git a/docs/source/data/defer.mdx b/docs/source/data/defer.mdx index 908ada61c6b..6b43736590b 100644 --- a/docs/source/data/defer.mdx +++ b/docs/source/data/defer.mdx @@ -7,7 +7,7 @@ description: Receive query response data incrementally Beginning with version `3.7.0`, Apollo Client provides preview support for [the `@defer` directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md). This directive enables your queries to receive data for specific fields _incrementally_, instead of receiving all field data at the same time. This is helpful whenever some fields in a query take much longer to resolve than others. -> For a query to defer fields successfully, the queried endpoint must _also_ support the `@defer` directive. Entity-based `@defer` support is also at the General Availability stage in [Apollo Router](/router/executing-operations/defer-support/) and is compatible with all [federation-compatible subgraph libraries](/federation/building-supergraphs/supported-subgraphs/). +> For a query to defer fields successfully, the queried endpoint must _also_ support the `@defer` directive. Entity-based `@defer` support is also at the General Availability stage in [GraphOS Router](/router/executing-operations/defer-support/) and is compatible with all [federation-compatible subgraph libraries](/federation/building-supergraphs/supported-subgraphs/). ## Example diff --git a/docs/source/data/document-transforms.mdx b/docs/source/data/document-transforms.mdx index d9acc5f8a54..96510c4e27e 100644 --- a/docs/source/data/document-transforms.mdx +++ b/docs/source/data/document-transforms.mdx @@ -2,9 +2,10 @@ title: Document transforms description: Make custom modifications to your GraphQL documents minVersion: 3.8.0 +api_doc: + - "@apollo/client!~DocumentTransformOptions:interface" --- - -import DocumentTransformOptions from '../../shared/document-transform-options.mdx'; +import { InterfaceDetails } from '../../shared/ApiDoc'; > This article assumes you're familiar with the [anatomy of a GraphQL query](https://www.apollographql.com/blog/graphql/basics/the-anatomy-of-a-graphql-query/) and the concept of an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree). To explore a GraphQL AST, visit [AST Explorer](https://astexplorer.net/). @@ -29,7 +30,7 @@ const client = new ApolloClient({ }); ``` -### Lifecycle +### Lifecycle Apollo Client runs document transforms before every GraphQL request for all operations. This extends to any API that performs a network request, such as the [`useQuery`](/react/api/react/hooks#usequery) hook or the [`refetch`](/react/api/core/ObservableQuery#refetch) function on [`ObservableQuery`](/react/api/core/ObservableQuery). @@ -111,11 +112,11 @@ const transformedDocument = visit(document, { ``` > Returning `undefined` from our `Field` visitor tells the `visit` function to leave the node unchanged. -Now that we've determined we are working with the `currentUser` field, we need to figure out if our `id` field is already part of the `currentUser` field's selection set. This ensures we don't accidentally select the field twice in our query. +Now that we've determined we are working with the `currentUser` field, we need to figure out if our `id` field is already part of the `currentUser` field's selection set. This ensures we don't accidentally select the field twice in our query. To do so, let's get the field's `selectionSet` property and loop over its `selections` property to determine if the `id` field is included. -It's important to note that a `selectionSet` may contain `selections` of both fields and fragments. Our implementation only needs to perform checks against fields, so we also check the selection's `kind` property. If we find a match on a field named `id`, we can stop traversal of the AST. +It's important to note that a `selectionSet` may contain `selections` of both fields and fragments. Our implementation only needs to perform checks against fields, so we also check the selection's `kind` property. If we find a match on a field named `id`, we can stop traversal of the AST. We will bring in both the [`Kind`](https://graphql.org/graphql-js/language/#kind) enum from `graphql-js`, which allows us to compare against the selection's `kind` property, and the [`BREAK`](https://graphql.org/graphql-js/language/#break) sentinel, which directs the `visit` function to stop traversal of the AST. @@ -160,7 +161,7 @@ const transformedDocument = visit(document, { Field(field) { // ... const idField = { - // ... + // ... }; return { @@ -224,7 +225,7 @@ const documentTransform = new DocumentTransform((document) => { ### Check our document transform -We can check our custom document transform by calling the `transformDocument` function and passing a GraphQL query to it. +We can check our custom document transform by calling the `transformDocument` function and passing a GraphQL query to it. ```ts import { print } from 'graphql'; @@ -331,7 +332,7 @@ Here `documentTransform1` is combined with `documentTransform2` into a single do #### A note about performance -Combining multiple transforms is a powerful feature that makes it easy to split up transform logic, which can boost maintainability. Depending on the implementation of your visitor, this can result in the traversal of the GraphQL document AST multiple times. Most of the time, this shouldn't be an issue. We recommend using the [`BREAK`](https://graphql.org/graphql-js/language/#break) sentinel from `graphql-js` to prevent unnecessary traversal. +Combining multiple transforms is a powerful feature that makes it easy to split up transform logic, which can boost maintainability. Depending on the implementation of your visitor, this can result in the traversal of the GraphQL document AST multiple times. Most of the time, this shouldn't be an issue. We recommend using the [`BREAK`](https://graphql.org/graphql-js/language/#break) sentinel from `graphql-js` to prevent unnecessary traversal. Suppose you are sending very large queries that require several traversals and have already optimized your visitors with the `BREAK` sentinel. In that case, it's best to combine the transforms into a single visitor that traverses the AST once. @@ -464,7 +465,7 @@ const documentTransform = new DocumentTransform( getCacheKey: (document) => { // Always run the transform function when `shouldCache` is `false` if (shouldCache(document)) { - return [document] + return [document] } } } @@ -507,7 +508,7 @@ const nonCachedTransform = new DocumentTransform(transform, { cache: false }); -const documentTransform = +const documentTransform = cachedTransform .concat(varyingTransform) .concat(conditionalCachedTransform) @@ -527,7 +528,7 @@ Thankfully, GraphQL Code Generator provides a [document transform](https://the-g ```ts title="codegen.ts" {2,12-18} import type { CodegenConfig } from '@graphql-codegen/cli'; import { documentTransform } from './path/to/your/transform'; - + const config: CodegenConfig = { schema: 'https://localhost:4000/graphql', documents: ['src/**/*.tsx'], @@ -574,7 +575,7 @@ Here is an example that uses a DSL-like directive that depends on a feature flag ```ts const query = gql` query MyQuery { - myCustomField @feature(name: "custom", version: 2) + myCustomField @feature(name: "custom", version: 2) } `; @@ -584,7 +585,7 @@ const documentTransform = new DocumentTransform((document) => { documentTransform.transformDocument(query); // query MyQuery($feature_custom_v2: Boolean!) { -// myCustomField @include(if: $feature_custom_v2) +// myCustomField @include(if: $feature_custom_v2) // } ``` @@ -592,4 +593,4 @@ documentTransform.transformDocument(query); ### Options - + diff --git a/docs/source/data/error-handling.mdx b/docs/source/data/error-handling.mdx index a6d25f9d08e..aee785d116f 100644 --- a/docs/source/data/error-handling.mdx +++ b/docs/source/data/error-handling.mdx @@ -154,7 +154,7 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); - if (networkError) console.log(`[Network error]: ${networkError}`); + if (networkError) console.error(`[Network error]: ${networkError}`); }); const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' }) diff --git a/docs/source/data/fragments.md b/docs/source/data/fragments.md index d90178584ef..c3fff7a39c0 100644 --- a/docs/source/data/fragments.md +++ b/docs/source/data/fragments.md @@ -105,7 +105,7 @@ import { ApolloClient, gql, InMemoryCache } from "@apollo/client"; import { createFragmentRegistry } from "@apollo/client/cache"; const client = new ApolloClient({ - uri: "http://localhost:4000/graphql" + uri: "http://localhost:4000/graphql", cache: new InMemoryCache({ fragments: createFragmentRegistry(gql` fragment ItemFragment on Item { @@ -148,7 +148,7 @@ import { ApolloClient, gql, InMemoryCache } from "@apollo/client"; import { createFragmentRegistry } from "@apollo/client/cache"; const client = new ApolloClient({ - uri: "http://localhost:4000/graphql" + uri: "http://localhost:4000/graphql", cache: new InMemoryCache({ fragments: createFragmentRegistry(gql` fragment ItemFragment on Item { diff --git a/docs/source/data/mutations.mdx b/docs/source/data/mutations.mdx index 29300dd1db3..74ca8df88dc 100644 --- a/docs/source/data/mutations.mdx +++ b/docs/source/data/mutations.mdx @@ -1,10 +1,12 @@ --- title: Mutations in Apollo Client description: Modify data with the useMutation hook +api_doc: + - "@apollo/client!MutationHookOptions:interface" + - "@apollo/client!MutationResult:interface" --- -import MutationOptions3 from '../../shared/mutation-options.mdx'; -import MutationResult3 from '../../shared/mutation-result.mdx'; +import { PropertySignatureTable } from '../../shared/ApiDoc'; Now that we've [learned how to query data](queries/) from our backend with Apollo Client, the natural next step is to learn how to _modify_ back-end data with **mutations**. @@ -152,7 +154,7 @@ if (error) return `Submission error! ${error.message}`; ### Resetting mutation status -The mutation result object returned by `useMutation` includes a [`reset` function](#reset): +The mutation result object returned by `useMutation` includes a [`reset` function](#mutationresult-interface-reset): ```js const [login, { data, loading, error, reset }] = useMutation(LOGIN_MUTATION); @@ -211,6 +213,8 @@ const [addTodo, { data, loading, error }] = useMutation(ADD_TODO, { }); ``` +> You can only refetch _active_ queries. Active queries are those used by components on the current page. If the data you want to update is not fetched by a component on the current page, it's best to [update your cache directly](#updating-the-cache-directly). + Each element in the `refetchQueries` array is one of the following: * A `DocumentNode` object parsed with the `gql` function @@ -378,7 +382,7 @@ detail with usage examples, see the [API reference](../api/react/hooks/). The `useMutation` hook accepts the following options: - + ### Result @@ -386,7 +390,7 @@ The `useMutation` result is a tuple with a mutate function in the first position You call the mutate function to trigger the mutation from your UI. - + ## Next steps diff --git a/docs/source/data/operation-best-practices.mdx b/docs/source/data/operation-best-practices.mdx index 8a2c9e88254..d7b26dcd3f6 100644 --- a/docs/source/data/operation-best-practices.mdx +++ b/docs/source/data/operation-best-practices.mdx @@ -160,87 +160,6 @@ query GetGlobalStatus { * If you have collections of components that _are_ always rendered together, you can use fragments to distribute the structure of a single query between them. See [Colocating fragments](./fragments/#colocating-fragments). * If you're querying a list field that returns more items than your component needs to render, you should [paginate that field](../pagination/overview/). - - -## Use fragments to encapsulate related sets of fields - -[GraphQL fragments](./fragments/) are sets of fields you can share across multiple operations. Here's an example declaration: - -```graphql -# Recommended ✅ -fragment NameParts on Person { - title - firstName - middleName - lastName -} -``` - -It's likely that multiple queries in an app require a person's full name. This `NameParts` fragment helps keep those queries consistent, readable, and short: - -```graphql -# Recommended ✅ -query GetAttendees($eventId: ID!) { - attendees(id: $eventId) { - id - rsvp - ...NameParts # Include all fields from the NameParts fragment - } -} -``` - -### Avoid excessive or illogical fragments - -If you use _too many_ fragments, your queries might become _less_ readable: - - - -```graphql -# Use caution ⚠️ -query GetAttendees($eventId: ID!) { - attendees(id: $eventId) { - id - rsvp - ...NameParts - profile { - ...VisibilitySettings - events { - ...EventSummary - } - avatar { - ...ImageDetails - } - } - } -} - -``` - - - -Additionally, only define fragments for sets of fields that share a logical semantic relationship. _Don't_ create a fragment just because multiple queries happen to share certain fields: - -```graphql -# Recommended ✅ -fragment NameParts on Person { - title - firstName - middleName - lastName -} - -# Not recommended ❌ -fragment SharedFields on Country { - population - neighboringCountries { - capital - rivers { - name - } - } -} -``` - ## Query global data and user-specific data separately Some fields return the exact same data regardless of which user queries them: diff --git a/docs/source/data/queries.mdx b/docs/source/data/queries.mdx index b0fe6140095..bd15ebf1a84 100644 --- a/docs/source/data/queries.mdx +++ b/docs/source/data/queries.mdx @@ -1,10 +1,12 @@ --- title: Queries description: Fetch data with the useQuery hook +api_doc: + - "@apollo/client!QueryHookOptions:interface" + - "@apollo/client!QueryResult:interface" --- -import QueryOptions from '../../shared/query-options.mdx'; -import QueryResult from '../../shared/query-result.mdx'; +import { PropertySignatureTable } from '../../shared/ApiDoc'; This article shows how to fetch GraphQL data in React with the `useQuery` hook and attach the result to your UI. You'll also learn how Apollo Client simplifies data management code by tracking error and loading states for you. @@ -20,11 +22,11 @@ This article also assumes that you've already set up Apollo Client and have wrap The `useQuery` [React hook](https://react.dev/reference/react) is the primary API for executing queries in an Apollo application. To run a query within a React component, call `useQuery` and pass it a GraphQL query string. When your component renders, `useQuery` returns an object from Apollo Client that contains `loading`, `error`, and `data` properties you can use to render your UI. -> **Note:** in Apollo Client >= 3.8, Suspense data fetching hooks are available for querying data within `` boundaries using React 18's new concurrent rendering model. For more information see Apollo Client's [Suspense docs](../suspense/). +> **Note:** in Apollo Client >= 3.8, Suspense data fetching hooks are available for querying data within `` boundaries using React 18's new concurrent rendering model. For more information see Apollo Client's [Suspense docs](./suspense/). Let's look at an example. First, we'll create a GraphQL query named `GET_DOGS`. Remember to wrap query strings in the `gql` function to parse them into query documents: -```jsx title="index.js" +```js title="index.js" import { gql, useQuery } from '@apollo/client'; const GET_DOGS = gql` @@ -155,7 +157,7 @@ function DogPhoto({ breed }) { return (
-
@@ -211,7 +213,7 @@ function DogPhoto({ breed }) { return (
-
@@ -267,6 +269,12 @@ The first item in `useLazyQuery`'s return tuple is the query function, and the s As shown above, you can pass options to the query function just like you pass them to `useLazyQuery` itself. If you pass a particular option to _both_, the value you pass to the query function takes precedence. This is a handy way to pass _default_ options to `useLazyQuery` and then customize those options in the query function. + + +Variables are merged by taking the `variables` passed as options to the hook and merging them with the `variables` passed to the query function. If you do not pass `variables` to the query function, only the `variables` passed to the hook are used in the query execution. + + + For a full list of supported options, see the [API reference](../api/react/hooks/#uselazyquery). ## Setting a fetch policy @@ -362,17 +370,17 @@ new ApolloClient({ options, // The original value of options.fetchPolicy, before nextFetchPolicy was // applied for the first time. - initialPolicy, + initialFetchPolicy, // The ObservableQuery associated with this client.watchQuery call. observable, } ) { // When variables change, the default behavior is to reset - // options.fetchPolicy to context.initialPolicy. If you omit this logic, + // options.fetchPolicy to context.initialFetchPolicy. If you omit this logic, // your nextFetchPolicy function can override this default behavior to // prevent options.fetchPolicy from changing in this case. if (reason === 'variables-changed') { - return initialPolicy; + return initialFetchPolicy; } if ( @@ -504,13 +512,13 @@ Most calls to `useQuery` can omit the majority of these options, but it's useful The `useQuery` hook accepts the following options: - + ### Result After being called, the `useQuery` hook returns a result object with the following properties. This object contains your query result, plus some helpful functions for refetching, dynamic polling, and pagination. - + ## Next steps diff --git a/docs/source/data/subscriptions.mdx b/docs/source/data/subscriptions.mdx index c4c177c9784..e6cd2fe29bc 100644 --- a/docs/source/data/subscriptions.mdx +++ b/docs/source/data/subscriptions.mdx @@ -1,10 +1,12 @@ --- title: Subscriptions description: Get real-time updates from your GraphQL server +api_doc: + - "@apollo/client!SubscriptionHookOptions:interface" + - "@apollo/client!SubscriptionResult:interface" --- -import SubscriptionOptions from '../../shared/subscription-options.mdx'; -import SubscriptionResult from '../../shared/subscription-result.mdx'; +import { PropertySignatureTable } from '../../shared/ApiDoc'; In addition to [queries](./queries/) and [mutations](./mutations/), GraphQL supports a third operation type: **subscriptions**. @@ -49,6 +51,51 @@ To use Apollo Client with a GraphQL endpoint that supports [multipart subscripti Aside from updating your client version, no additional configuration is required! Apollo Client automatically sends the required headers with the request if the terminating `HTTPLink` is passed a subscription operation. +#### Usage with Relay or urql + +To consume a multipart subscription over HTTP in an app using Relay or urql, Apollo Client provides network layer adapters that handle the parsing of the multipart response format. + +##### Relay + +```ts +import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/relay"; +import { Environment, Network, RecordSource, Store } from "relay-runtime"; + +const fetchMultipartSubs = createFetchMultipartSubscription( + "https://api.example.com" +); + +const network = Network.create(fetchQuery, fetchMultipartSubs); + +export const RelayEnvironment = new Environment({ + network, + store: new Store(new RecordSource()), +}); +``` + +#### urql + +```ts +import { createFetchMultipartSubscription } from "@apollo/client/utilities/subscriptions/urql"; +import { Client, fetchExchange, subscriptionExchange } from "@urql/core"; + +const url = "https://api.example.com"; + +const multipartSubscriptionForwarder = createFetchMultipartSubscription( + url +); + +const client = new Client({ + url, + exchanges: [ + fetchExchange, + subscriptionExchange({ + forwardSubscription: multipartSubscriptionForwarder, + }), + ], +}); +``` + ## Defining a subscription You define a subscription on both the server side and the client side, just like you do for queries and mutations. @@ -328,13 +375,13 @@ export function CommentsPage({subscribeToNewComments}) { The `useSubscription` Hook accepts the following options: - + ### Result After being called, the `useSubscription` Hook returns a result object with the following properties: - + ## The older `subscriptions-transport-ws` library diff --git a/docs/source/data/suspense.mdx b/docs/source/data/suspense.mdx index d53d913c26b..793d5eb3c2c 100644 --- a/docs/source/data/suspense.mdx +++ b/docs/source/data/suspense.mdx @@ -1,6 +1,6 @@ --- title: Suspense -description: Using React 18 Suspense features with Apollo Client +description: Use React 18 Suspense features with Apollo Client minVersion: 3.8.0 --- @@ -48,6 +48,7 @@ const GET_DOG_QUERY: TypedDocumentNode = gql` # properly cached. id name + breed } } `; @@ -69,13 +70,21 @@ function Dog({ id }: DogProps) { } ``` -> **Note:** This example manually defines TypeScript interfaces for `Data` and `Variables` as well as the type for `GET_DOG_QUERY` using `TypedDocumentNode`. [GraphQL Code Generator](https://www.the-guild.dev/graphql/codegen) is a popular tool that creates these type definitions automatically for you. See the reference on [Using TypeScript](../development-testing/static-typing) for more information on integrating GraphQL Code Generator with Apollo Client. + + +This example manually defines TypeScript interfaces for `Data` and `Variables` as well as the type for `GET_DOG_QUERY` using `TypedDocumentNode`. [GraphQL Code Generator](https://www.the-guild.dev/graphql/codegen) is a popular tool that creates these type definitions automatically for you. See the reference on [Using TypeScript](../development-testing/static-typing) for more information on integrating GraphQL Code Generator with Apollo Client. + + In this example, our `App` component renders a `Dog` component which fetches the record for a single dog via `useSuspenseQuery`. When React attempts to render `Dog` for the first time, the cache is unable to fulfill the request for the `GetDog` query, so `useSuspenseQuery` initiates a network request. `Dog` suspends while the network request is pending, triggering the nearest `Suspense` boundary _above_ the suspended component in `App` which renders our "Loading..." fallback. Once the network request is complete, `Dog` renders with the newly cached `name` for Mozzarella the Corgi. You may have noticed that `useSuspenseQuery` does not return a `loading` boolean. That's because the component calling `useSuspenseQuery` always suspends when fetching data. A corollary is that when it _does_ render, `data` is always defined! In the Suspense paradigm, fallbacks that exist **outside of suspended components** replace the loading states components were previously responsible for rendering themselves. -> **Note for TypeScript users**: Since `GET_DOG_QUERY` is a `TypedDocumentNode` in which we have specified the result type via `Data` generic type argument, the TypeScript type for `data` returned by `useSuspenseQuery` reflects that! This means that `data` is guaranteed to be defined when `Dog` renders, and that `data.dog` has the shape `{ id: string; name: string; breed: string; }`. + + +**For TypeScript users**: Since `GET_DOG_QUERY` is a `TypedDocumentNode` in which we have specified the result type via `Data` generic type argument, the TypeScript type for `data` returned by `useSuspenseQuery` reflects that! This means that `data` is guaranteed to be defined when `Dog` renders, and that `data.dog` has the shape `{ id: string; name: string; breed: string; }`. + + ### Changing variables @@ -121,7 +130,7 @@ function App() { onChange={(e) => setSelectedDog(e.target.value)} > {data.dogs.map(({ id, name }) => ( - + ))} Loading...}> @@ -229,7 +238,11 @@ function App() { When the cache contains partial data, you may prefer to render that data immediately without suspending. To do this, use the `returnPartialData` option. -> **Note:** This option only works when combined with either the `cache-first` (default) or `cache-and-network` fetch policy. `cache-only` is not currently supported by `useSuspenseQuery`. For details on these fetch policies, see [Setting a fetch policy](/react/data/queries/#setting-a-fetch-policy). + + +This option only works when combined with either the `cache-first` (default) or `cache-and-network` fetch policy. `cache-only` isn't currently supported by `useSuspenseQuery`. For details on these fetch policies, see [Setting a fetch policy](/react/data/queries/#setting-a-fetch-policy). + + Let's update our example to use the partial cache data and render immediately: @@ -256,7 +269,7 @@ const PARTIAL_GET_DOG_QUERY: TypedDocumentNode< // Write partial data for Buck to the cache // so it is available when Dog renders client.writeQuery({ - query: GET_DOG_QUERY_PARTIAL, + query: PARTIAL_GET_DOG_QUERY, variables: { id: "1" }, data: { dog: { id: "1", name: "Buck" } }, }); @@ -290,13 +303,21 @@ In this example, we write partial data to the cache for Buck in order to show th On first render, Buck's name is displayed after the `Name` label, followed by the `Breed` label with no value. Once the missing fields have loaded, `useSuspenseQuery` triggers a re-render and Buck's breed is displayed. -> **Note for TypeScript users**: With `returnPartialData` set to `true`, the returned type for the `data` property marks all fields in the query type as [optional](https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties). Apollo Client cannot accurately determine which fields are present in the cache at any given time when returning partial data. + + +**For TypeScript users**: With `returnPartialData` set to `true`, the returned type for the `data` property marks all fields in the query type as [optional](https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties). Apollo Client cannot accurately determine which fields are present in the cache at any given time when returning partial data. + + ### Error handling By default, both network errors and GraphQL errors are thrown by `useSuspenseQuery`. These errors are caught and displayed by the closest [error boundary](https://react.dev/reference/react/Component#static-getderivedstatefromerror). -> **Note:** An error boundary is a **class component** that implements `static getDerivedStateFromError`. For more information, see the React docs for [catching rendering errors with an error boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary). + + +An error boundary is a **class component** that implements `static getDerivedStateFromError`. For more information, see the React docs for [catching rendering errors with an error boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary). + + Let's create a basic error boundary that renders an error UI when errors are thrown by our query: @@ -321,7 +342,11 @@ class ErrorBoundary extends React.Component { } ``` -> **Note:** In a real application, your error boundary might need a more robust implementation. Consider using a library like [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) when you need a high degree of flexibility and reusability. + + +In a real application, your error boundary might need a more robust implementation. Consider using a library like [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) when you need a high degree of flexibility and reusability. + + When the `GET_DOG_QUERY` inside of the `Dog` component returns a GraphQL error or a network error, `useSuspenseQuery` throws the error and the nearest error boundary renders its fallback component. @@ -359,7 +384,11 @@ function App() { Here, we're using our `ErrorBoundary` component and placing it **outside** of our `Dog` component. Now, when the `useSuspenseQuery` hook inside the `Dog` component throws an error, the `ErrorBoundary` catches it and displays the `fallback` element we've provided. -> **Note:** When working with many React frameworks, you may see an error dialog overlay in development mode when errors are thrown, even _with_ an error boundary. This is done to avoid the error being missed by developers. + + +When working with many React frameworks, you may see an error dialog overlay in development mode when errors are thrown, even _with_ an error boundary. This is done to avoid the error being missed by developers. + + #### Rendering partial data alongside errors @@ -408,7 +437,7 @@ function Dog({ id, queryRef }: DogProps) { } interface BreedsProps { - queryRef: QueryReference; + queryRef: QueryRef; } function Breeds({ queryRef }: BreedsProps) { @@ -426,12 +455,228 @@ We begin fetching our `GET_BREEDS_QUERY` when the `App` component renders. The n When the network request for `GET_DOG_QUERY` completes, the `Dog` component unsuspends and continues rendering, reaching the `Breeds` component. Since our `GET_BREEDS_QUERY` request was initiated higher up in our component tree using `useBackgroundQuery`, the network request for `GET_BREEDS_QUERY` **has already completed**! When the `Breeds` component reads the data from the `queryRef` provided by `useBackgroundQuery`, it avoids suspending and renders immediately with the fetched data. -> **Note:** When the `GET_BREEDS_QUERY` takes _longer_ to fetch than our `GET_DOG_QUERY`, `useReadQuery` suspends and the Suspense fallback inside of the `Dog` component is rendered instead. + + +When the `GET_BREEDS_QUERY` takes _longer_ to fetch than our `GET_DOG_QUERY`, `useReadQuery` suspends and the Suspense fallback inside of the `Dog` component is rendered instead. + + #### A note about performance The `useBackgroundQuery` hook used in a parent component is responsible for kicking off fetches, but doesn't deal with reading or rendering data. This is delegated to the `useReadQuery` hook used in a child component. This separation of concerns provides a nice performance benefit because cache updates are observed by `useReadQuery` and re-render only the child component. You may find this as a useful tool to optimize your component structure to avoid unnecessarily re-rendering parent components when cache data changes. + + +### Fetching in response to user interaction + + + +The `useSuspenseQuery` and `useBackgroundQuery` hooks let us load data as soon as the component calling the hook mounts. But what about loading a query in response to user interaction? For example, we may want to start loading some data when a user hovers on a link. + +Available as of Apollo Client `3.9.0`, the `useLoadableQuery` hook initiates a network request in response to user interaction. + +`useLoadableQuery` returns both an execution function and a `queryRef`. The execution function initiates a network request when called with the provided variables. Like `useBackgroundQuery`, passing the `queryRef` to `useReadQuery` in a child component suspends the child component until the query finishes loading. + + + +The `queryRef` is `null` until the execution function is called for the first time. For this reason, any child components attempting to read data using the `queryRef` should be conditionally rendered. + + + +Let's update our example to start loading the dog's details as a result of selecting from a dropdown. + +```tsx {3,8,13,22,29} +import { + // ... + useLoadableQuery +} from '@apollo/client'; + +function App() { + const { data } = useSuspenseQuery(GET_DOGS_QUERY); + const [loadDog, queryRef] = useLoadableQuery(GET_DOG_QUERY); + + return ( + <> + + Loading...}> + {queryRef && } + + + ); +} + +function Dog({ queryRef }: DogProps) { + const { data } = useReadQuery(queryRef) + + return ( + <> +
Name: {data.dog.name}
+
Breed: {data.dog.breed}
+ + ); +} +``` + +We begin fetching our `GET_DOG_QUERY` by calling the `loadDog` function inside of the `onChange` handler function when a dog is selected. Once the network request is initiated, the `queryRef` is no longer `null`, rendering the `Dog` component. + +`useReadQuery` suspends the `Dog` component while the network request finishes, then returns `data` to the component. As a result of this change, we've also eliminated the need to track the selected dog `id` in component state. + + + +### Initiating queries outside React + + + +Starting with Apollo Client `3.9.0`, queries can be initiated outside of React. This allows your app to begin fetching data before React renders your components, and can provide performance benefits. + +To preload queries, you first need to create a preload function with `createQueryPreloader`. `createQueryPreloader` takes an `ApolloClient` instance as an argument and returns a function that, when called, initiates a network request. + + + +Consider exporting your preload function along with your `ApolloClient` instance. This allows you to import that function directly without having to pass around your `ApolloClient` instance each time you preload a query. + + + +The preload function returns a `queryRef` that is passed to `useReadQuery` to read the query data and suspend the component while loading. `useReadQuery` ensures that your component is kept up-to-date with cache updates for the preloaded query. + + +Let's update our example to start loading the `GET_DOGS_QUERY` before our app is rendered. + +```tsx {3,9-10,13,36,38} +import { + // ... + createQueryPreloader +} from '@apollo/client'; + +// This `preloadQuery` function does not have to be created each time you +// need to preload a new query. You may prefer to export this function +// alongside your client instead. +const preloadQuery = createQueryPreloader(client); +const preloadedQueryRef = preloadQuery(GET_DOGS_QUERY); + +function App() { + const { data } = useReadQuery(preloadedQueryRef); + const [queryRef, loadDog] = useLoadableQuery(GET_DOG_QUERY) + + return ( + <> + + Loading...}> + + + + ); +} + +const root = createRoot(document.getElementById('app')); + +root.render( + + Loading...}> + + + +); +``` + +We begin loading data as soon as our `preloadQuery` function is called rather than waiting for React to render our `` component. Because the `preloadedQueryRef` is passed to `useReadQuery` in our `App` component, we still get the benefit of suspense and cache updates. + + + +Unmounting components that contain preloaded queries is safe and disposes of the `queryRef`. When the component re-mounts, `useReadQuery` automatically resubscribes to the `queryRef` and any cache updates that occurred in the interim are read immediately as if the preloaded query had never been unmounted. + + + +#### Usage with data loading routers + +Popular routers such as [React Router](https://reactrouter.com/en/main) and [TanStack Router](https://tanstack.com/router) provide APIs to load data before route components are rendered. One example is the [`loader` function](https://reactrouter.com/en/main/route/route#loader) from React Router. + +Loading data before rendering route components is especially useful for nested routes where data loading is parallelized. It prevents situations where parent route components might otherwise suspend and create request waterfalls for child route components. + +`preloadQuery` pairs nicely with these router APIs as it lets you take advantage of those optimizations without sacrificing the ability to rerender cache updates in your route components. + +Let's update our example using React Router's `loader` function to begin loading data when we transition to our route. + +```ts {4,8-9} +import { useLoaderData } from 'react-router-dom'; + +export function loader() { + return preloadQuery(GET_DOGS_QUERY); +} + +export function RouteComponent() { + const queryRef = useLoaderData(); + const { data } = useReadQuery(queryRef); + + return ( + // ... + ); +} +``` + +> The `loader` function is available in React Router versions 6.4 and above. + +React Router calls the `loader` function which we use to begin loading the `GET_DOG_QUERY` query by calling the `preloadQuery` function. The `queryRef` created by `preloadQuery` is returned from the `loader` function making it accessible in the route component. + +When the route component renders, we access the `queryRef` from the `useLoaderData` hook, which is then passed to `useReadQuery`. We get the benefit of loading our data early in the routing lifecycle, and the route component maintains the ability to rerender with cache updates. + + + +The `preloadQuery` function only works with client-side routing. The `queryRef` returned from `preloadQuery` isn't serializable across the wire and as such, won't work with routers that fetch on the server such as [Remix](https://remix.run/). + + + +#### Preventing route transitions until the query is loaded + +By default, `preloadQuery` works similarly to a [deferred loader](https://reactrouter.com/en/main/guides/deferred): the route transitions immediately and the incoming page that's attempting to read the data via `useReadQuery` suspends until the network request finishes. + +But what if we want to prevent the route from transitioning until the data is fully loaded? `queryRef`'s `toPromise` method provides access to a promise that resolves when the network request has completed. This promise resolves with the `queryRef` itself, making it easy to use with hooks such as `useLoaderData`. + +Here's an example: + +```ts +export async function loader() { + const queryRef = await preloadQuery(GET_DOGS_QUERY).toPromise(); + + return queryRef; +} + +// You may also return the promise directly from loader. +// This is equivalent to the above. +export async function loader() { + return preloadQuery(GET_DOGS_QUERY).toPromise(); +} + +export function RouteComponent() { + const queryRef = useLoaderData(); + const { data } = useReadQuery(queryRef); + + // ... +} +``` + +This instructs React Router to wait for the query to finish loading before the route transitions. When the route transitions after the promise resolves, the data is rendered immediately without the need to show a loading fallback in the route component. + +#### Why prevent access to `data` in `toPromise`? + +You may be wondering why we resolve `toPromise` with the `queryRef` itself, rather than the data loaded from the query. We want to encourage you to leverage `useReadQuery` to avoid missing out on cache updates for your query. If `data` were available, it would be tempting to consume it in your `loader` functions and expose it to your route components. Doing so means missing out on cache updates. + +If you need access to raw query data in your `loader` functions, use [`client.query()`](../api/core/ApolloClient#query) directly. + ### Refetching and pagination Apollo's Suspense data fetching hooks return functions for refetching query data via the `refetch` function, and fetching additional pages of data via the `fetchMore` function. @@ -446,7 +691,7 @@ import { useReadQuery, gql, TypedDocumentNode, - QueryReference, + QueryRef, } from "@apollo/client"; function App() { @@ -512,6 +757,99 @@ function Breeds({ queryRef, isPending }: BreedsProps) { In this example, our `App` component renders a `Dog` component that fetches a single dog's record via `useSuspenseQuery`. When React attempts to render `Dog` for the first time, the cache can't fulfill the request for the `GetDog` query, so `useSuspenseQuery` initiates a network request. `Dog` suspends while the network request is pending, triggering the nearest `Suspense` boundary _above_ the suspended component in `App` which renders our "Loading..." fallback. Once the network request is complete, `Dog` renders with the newly cached `name` for the dog whose `id="3"`: Mozzarella the Corgi. +#### Usage with query preloading + +When loading queries [outside React](#initiating-queries-outside-react), the `preloadQuery` function returns a `queryRef` with no access to `refetch` or `fetchMore` functions. This presents a challenge when you need to refetch or paginate the preloaded query. + +You can gain access to `refetch` and `fetchMore` functions by using the `useQueryRefHandlers` hook. This hook integrates with React transitions, giving you the ability to refetch or paginate without showing the loading fallback. + +Let's update our example to preload our `GET_BREEDS_QUERY` outside React and use the `useQueryRefHandlers` hook to refetch our query. + +```tsx {4,7,11} +// ... +import { + // ... + useQueryRefHandlers, +} from "@apollo/client"; + +const queryRef = preloadQuery(GET_BREEDS_QUERY); + +function App() { + const [isPending, startTransition] = useTransition(); + const { refetch } = useQueryRefHandlers(queryRef); + + function handleRefetch() { + startTransition(() => { + refetch(); + }); + }; + + return ( + Loading...}> + + + ); +} + +// ... +``` + +We begin loading our `GET_BREEDS_QUERY` outside of React with the `preloadQuery` function. We pass the `queryRef` returned from `preloadQuery` to the `useQueryRefHandlers` hook which provides us with a `refetch` function we can use to refetch the query when the button is clicked. + +#### Using `useQueryRefHandlers` with query refs produced from other Suspense hooks + +`useQueryRefHandlers` can also be used in combination with any hook that returns a `queryRef`, such as `useBackgroundQuery` or `useLoadableQuery`. This is useful when you need access to the `refetch` and `fetchMore` functions in components where the `queryRef` is passed through deeply. + +Let's update our example to use `useBackgroundQuery` again and see how we can access a `refetch` function in our `Dog` component using `useQueryRefHandlers`: + +```tsx {15,16,18-22,30} +function App() { + const [queryRef] = useBackgroundQuery(GET_BREEDS_QUERY); + + return ( + Loading...}> + + + ); +} + +function Dog({ id, queryRef }: DogProps) { + const { data } = useSuspenseQuery(GET_DOG_QUERY, { + variables: { id }, + }); + const [isPending, startTransition] = useTransition(); + const { refetch } = useQueryRefHandlers(queryRef); + + function handleRefetch() { + startTransition(() => { + refetch(); + }); + }; + + return ( + <> + Name: {data.dog.name} + Loading breeds...}> + + + + + ); +} + +// ... +``` + + + +Using the handlers returned from `useQueryRefHandlers` does not prevent you from using the handlers produced by query ref hooks. You can use the handlers in both locations, with or without React transitions to produce the desired result. + + + ## Distinguishing between queries with `queryKey` Apollo Client uses the combination of `query` and `variables` to uniquely identify each query when using Apollo's Suspense data fetching hooks. @@ -527,7 +865,7 @@ For more information, see the [`useSuspenseQuery`](../api/react/hooks/#usesuspen While `useSuspenseQuery` and `useBackgroundQuery` both have a `skip` option, that option is only present to ease migration from `useQuery` with as few code changes as possible. It should not be used in the long term. -Instead, you should use [`skipToken`](/react/api/react/hooks#skiptoken`): +Instead, you should use [`skipToken`](/react/api/react/hooks#skiptoken): ```js title="Recommended usage of skipToken with useSuspenseQuery" import { skipToken, useSuspenseQuery } from '@apollo/client'; @@ -575,6 +913,14 @@ More details on `useSuspenseQuery`'s API can be found in [its API docs](../api/r More details on `useBackgroundQuery`'s API can be found in [its API docs](../api/react/hooks/#usebackgroundquery). +## useLoadableQuery API + +More details on `useLoadableQuery`'s API can be found in [its API docs](../api/react/hooks/#useloadablequery). + +## useQueryRefHandlers API + +More details on `useQueryRefHandlers`'s API can be found in [its API docs](../api/react/hooks/#usequeryrefhandlers). + ## useReadQuery API More details on `useReadQuery`'s API can be found in [its API docs](../api/react/hooks/#usereadquery). diff --git a/docs/source/development-testing/reducing-bundle-size.mdx b/docs/source/development-testing/reducing-bundle-size.mdx index 58ad4571428..11479d47ae8 100644 --- a/docs/source/development-testing/reducing-bundle-size.mdx +++ b/docs/source/development-testing/reducing-bundle-size.mdx @@ -3,8 +3,10 @@ title: Reducing bundle size description: Squeeze those last few bytes out of your production build --- -Two quick configuration changes can help your reduce your bundle size: turning off Apollo Client's development mode and picking a consistent style for your imports. -Every byte counts in optimizing your app's performance. +Every byte counts in optimizing your app's performance. This page covers some configuration changes that can help reduce Apollo Client's bundle size: + +- Turn off Apollo Client's development mode +- Pick a consistent style for your imports ## Turning off development mode @@ -183,4 +185,22 @@ With many modern bundlers, it should not matter which of these styles you choose It is important to keep in mind though that bundlers are complex and it might make a difference - especially when your bundler picks up the CommonJS artifacts instead of the ESM artifacts.
Every bundling setup is different and we cannot guarantee which style results in the smallest bundle size. We recommend trying out these styles in a small setup to determine which results in the best outcome in your environment. -**Note:** some entry points are not part of the "main" entrypoint `'@apollo/client'` and can only be imported directly (e.g. from `'@apollo/client/link/batch'`). It's perfectly fine to use these, even when using the "main" entrypoint. + + +Some entry points are not part of the "main" entry point `'@apollo/client'` and can only be imported directly, for example, from `'@apollo/client/link/batch'`. It's fine to use these, even when using the "main" entry point. + + + +## Why have a larger library in the first place? + +Apollo Client is more than a data fetcher. It's a normalized request and response cache, state manager, and React component integration, including React testing utilities. + +To build a similar experience with other libraries, you need to write custom logic, libraries, and component wrappers. Scaling this custom code to all your app components often creates a bundle larger than Apollo Client's. + +A custom implementation also means extra maintenance work for your team. By using Apollo Client, you hand off that ownership to a team specializing in GraphQL clients. + +### Legacy support + +Apollo Client v3 supports legacy JavaScript syntax. It also includes support for some now deprecated features. Similarly, v3 offers broad browser compatibility, extending back to IE11. + +The next major version will prioritize contemporary browser support. This transition will streamline the client by removing unnecessary polyfills and phasing out support for legacy syntax and features. Ultimately, these changes reduce bundle size but require a major version release. diff --git a/docs/source/development-testing/schema-driven-testing.mdx b/docs/source/development-testing/schema-driven-testing.mdx new file mode 100644 index 00000000000..f69294c0ff6 --- /dev/null +++ b/docs/source/development-testing/schema-driven-testing.mdx @@ -0,0 +1,560 @@ +--- +title: Schema-driven testing +description: Using createTestSchema and associated APIs +minVersion: 3.10.0 +--- + +This article describes best practices for writing integration tests using testing utilities released as experimental in v3.10. These testing tools allow developers to execute queries against a schema configured with mock resolvers and default scalar values in order to test an entire Apollo Client application, including the [link chain](/react/api/link/introduction). + +## Guiding principles + +Kent C. Dodds [said it best](https://twitter.com/kentcdodds/status/977018512689455106): + +> The more your tests resemble the way your software is used, the more confidence they can give you. + +When it comes to testing applications built with Apollo Client, this means validating the code path your users' requests will travel from the UI to the network layer and back. + +Unit-style testing with [`MockedProvider`](/react/development-testing/testing) can be useful for testing individual components—or even entire pages or React subtrees—in isolation by mocking the expected response data for individual operations. However, it's important to also test the integration of your components with the network layer. That's where schema-driven testing comes in. + +> This page is heavily inspired by the excellent [Redux documentation](https://redux.js.org/usage/writing-tests#guiding-principles); the same principles apply to Apollo Client. + +## `createTestSchema` and `createSchemaFetch` + +### Installation + +First, ensure you have installed Apollo Client v3.10 or greater. Then, install the following peer dependencies: + +```bash +npm i @graphql-tools/merge @graphql-tools/schema @graphql-tools/utils undici --save-dev +``` + +Consider a React application that fetches a list of products from a GraphQL server: + + + +```tsx title="products.tsx" +import { gql, TypedDocumentNode, useSuspenseQuery } from "@apollo/client"; + +type ProductsQuery = { + products: Array<{ + __typename: "Product"; + id: string; + title: string; + mediaUrl: string; + }>; +}; + +const PRODUCTS_QUERY: TypedDocumentNode = gql` + query ProductsQuery { + products { + id + title + mediaUrl + } + } +`; + +export function Products() { + const { data } = useSuspenseQuery(PRODUCTS_QUERY); + + return ( +
+ {data.products.map((product) => ( +

+ + {product.title} - {product.id} + +

+ ))} +
+ ); +} +``` + +
+ +Now let's write some tests using a test schema created with the `createTestSchema` utility that can then be used to create a mock fetch implementation with `createSchemaFetch`. + +### Configuring your test environment + +First, some Node.js globals will need to be polyfilled in order for JSDOM tests to run correctly. Create a file called e.g. `jest.polyfills.js`: + +```js title="jest.polyfills.js" +/** + * @note The block below contains polyfills for Node.js globals + * required for Jest to function when running JSDOM tests. + * These have to be require's and have to be in this exact + * order, since "undici" depends on the "TextEncoder" global API. + */ + +const { TextDecoder, TextEncoder } = require("node:util"); +const { ReadableStream } = require("node:stream/web"); +const { clearImmediate } = require("node:timers"); +const { performance } = require("node:perf_hooks"); + +Object.defineProperties(globalThis, { + TextDecoder: { value: TextDecoder }, + TextEncoder: { value: TextEncoder }, + ReadableStream: { value: ReadableStream }, + performance: { value: performance }, + clearImmediate: { value: clearImmediate }, +}); + +const { Blob, File } = require("node:buffer"); +const { fetch, Headers, FormData, Request, Response } = require("undici"); + +Object.defineProperties(globalThis, { + fetch: { value: fetch, writable: true }, + Response: { value: Response }, + Blob: { value: Blob }, + File: { value: File }, + Headers: { value: Headers }, + FormData: { value: FormData }, + Request: { value: Request }, +}); + +// Note: if your environment supports it, you can use the `using` keyword +// but must polyfill Symbol.dispose here with Jest versions <= 29 +// where Symbol.dispose is not defined +// +// Jest bug: https://github.com/jestjs/jest/issues/14874 +// Fix is available in https://github.com/jestjs/jest/releases/tag/v30.0.0-alpha.3 +if (!Symbol.dispose) { + Object.defineProperty(Symbol, "dispose", { + value: Symbol("dispose"), + }); +} +if (!Symbol.asyncDispose) { + Object.defineProperty(Symbol, "asyncDispose", { + value: Symbol("asyncDispose"), + }); +} +``` + +Now, in a `jest.config.ts` or `jest.config.js` file, add the following configuration: + +```ts title="jest.config.ts" +import type { Config } from "jest"; + +const config: Config = { + globals: { + "globalThis.__DEV__": JSON.stringify(true), + }, + testEnvironment: "jsdom", + setupFiles: ["./jest.polyfills.js"], + // You may also have an e.g. setupTests.ts file here + setupFilesAfterEnv: ["/setupTests.ts"], + // If you're using MSW, opt out of the browser export condition for MSW tests + // For more information, see: https://github.com/mswjs/msw/issues/1786#issuecomment-1782559851 + testEnvironmentOptions: { + customExportConditions: [""], + }, + // If you plan on importing .gql/.graphql files in your tests, transform them with @graphql-tools/jest-transform + transform: { + "\\.(gql|graphql)$": "@graphql-tools/jest-transform", + }, +}; + +export default config; +``` + +In the example `setupTests.ts` file below, `@testing-library/jest-dom` is imported to allow the use of custom `jest-dom` matchers (see the [`@testing-library/jest-dom` documentation](https://github.com/testing-library/jest-dom?tab=readme-ov-file#usage) for more information) and fragment warnings are disabled which can pollute the test output: + +```ts title="setupTests.ts" +import "@testing-library/jest-dom"; +import { gql } from "@apollo/client"; + +gql.disableFragmentWarnings(); +``` + +### Testing with MSW + +Now, let's write a test for the `Products` component using [MSW](https://mswjs.io/). + +MSW is a powerful tool for intercepting network traffic and mocking responses. Read more about its design and philosophy [here](https://mswjs.io/blog/why-mock-service-worker/). + +MSW has the concept of [handlers](https://mswjs.io/docs/best-practices/structuring-handlers/) that allow network requests to be intercepted. Let's create a handler that will intercept all GraphQL operations: + +```ts title="src/__tests__/handlers.ts" +import { graphql, HttpResponse } from "msw"; +import { execute } from "graphql"; +import type { ExecutionResult } from "graphql"; +import type { ObjMap } from "graphql/jsutils/ObjMap"; +import { gql } from "@apollo/client"; +import { createTestSchema } from "@apollo/client/testing/experimental"; +import { makeExecutableSchema } from "@graphql-tools/schema"; +import graphqlSchema from "../../../schema.graphql"; + +// First, create a static schema... +const staticSchema = makeExecutableSchema({ typeDefs: graphqlSchema }); + +// ...which is then passed as the first argument to `createTestSchema` +// along with mock resolvers and default scalar values. +export let testSchema = createTestSchema(staticSchema, { + resolvers: { + Query: { + products: () => [ + { + id: "1", + title: "Blue Jays Hat", + }, + ], + }, + }, + scalars: { + Int: () => 6, + Float: () => 22.1, + String: () => "string", + }, +}); + +export const handlers = [ + // Intercept all GraphQL operations and return a response generated by the + // test schema. Add additional handlers as needed. + graphql.operation, ObjMap>>( + async ({ query, variables, operationName }) => { + const document = gql(query); + + const result = await execute({ + document, + operationName, + schema: testSchema, + variableValues: variables, + }); + + return HttpResponse.json(result); + } + ), +]; +``` + +MSW can be used in [the browser](https://mswjs.io/docs/integrations/browser), in [Node.js](https://mswjs.io/docs/integrations/node) and in [React Native](https://mswjs.io/docs/integrations/react-native). Since this example is using Jest and JSDOM to run tests in a Node.js environment, let's configure the server per the [Node.js integration guide](https://mswjs.io/docs/integrations/node): + +```ts title="src/__tests__/server.ts" +import { setupServer } from "msw/node"; +import { handlers } from "./handlers"; + +// This configures a request mocking server with the given request handlers. +export const server = setupServer(...handlers); +``` + +Finally, let's do server set up and teardown in the `setupTests.ts` file created in the previous step: + +```ts title="setupTests.ts" {6-8} +import "@testing-library/jest-dom"; +import { gql } from "@apollo/client"; + +gql.disableFragmentWarnings(); + +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); +afterAll(() => server.close()); +afterEach(() => server.resetHandlers()); +``` + +Finally, let's write some tests 🎉 + +```tsx title="src/__tests__/products.test.tsx" +import { Suspense } from "react"; +import { render as rtlRender, screen } from "@testing-library/react"; +import { + ApolloClient, + ApolloProvider, + NormalizedCacheObject, +} from "@apollo/client"; +import { testSchema } from "./handlers"; +import { Products } from "../products"; +// This should be a function that returns a new ApolloClient instance +// configured just like your production Apollo Client instance - see the FAQ. +import { makeClient } from "../client"; + +const render = (renderedClient: ApolloClient) => + rtlRender( + + + + + + ); + +describe("Products", () => { + test("renders", async () => { + render(makeClient()); + + await screen.findByText("Loading..."); + + // This is the data from our initial mock resolver in the test schema + // defined in the handlers file 🎉 + expect(await screen.findByText(/blue jays hat/i)).toBeInTheDocument(); + }); + + test("allows resolvers to be updated via .add", async () => { + // Calling .add on the test schema will update the resolvers + // with new data + testSchema.add({ + resolvers: { + Query: { + products: () => { + return [ + { + id: "2", + title: "Mets Hat", + }, + ]; + }, + }, + }, + }); + + render(makeClient()); + + await screen.findByText("Loading..."); + + // Our component now renders the new data from the updated resolver + await screen.findByText(/mets hat/i); + }); + + test("handles test schema resetting via .reset", async () => { + // Calling .reset on the test schema will reset the resolvers + testSchema.reset(); + + render(makeClient()); + + await screen.findByText("Loading..."); + + // The component now renders the initial data configured on the test schema + await screen.findByText(/blue jays hat/i); + }); +}); +``` + +### Testing by mocking fetch with `createSchemaFetch` + +First, import `createSchemaFetch` and `createTestSchema` from the new `@apollo/client/testing` entrypoint. Next, import a local copy of your graph's schema: jest should be configured to transform `.gql` or `.graphql` files using `@graphql-tools/jest-transform` (see the `jest.config.ts` example configuration above.) + +Here's how an initial set up of the test file might look: + +```tsx title="products.test.tsx" +import { + createSchemaFetch, + createTestSchema, +} from "@apollo/client/testing/experimental"; +import { makeExecutableSchema } from "@graphql-tools/schema"; +import { render as rtlRender, screen } from "@testing-library/react"; +import graphqlSchema from "../../../schema.graphql"; +// This should be a function that returns a new ApolloClient instance +// configured just like your production Apollo Client instance - see the FAQ. +import { makeClient } from "../../client"; +import { ApolloProvider, NormalizedCacheObject } from "@apollo/client"; +import { Products } from "../../products"; +import { Suspense } from "react"; + +// First, let's create an executable schema... +const staticSchema = makeExecutableSchema({ typeDefs: graphqlSchema }); + +// which is then passed as the first argument to `createTestSchema`. +const schema = createTestSchema(staticSchema, { + // Next, let's define mock resolvers + resolvers: { + Query: { + products: () => + Array.from({ length: 5 }, (_element, id) => ({ + id, + mediaUrl: `https://example.com/image${id}.jpg`, + })), + }, + }, + // ...and default scalar values + scalars: { + Int: () => 6, + Float: () => 22.1, + String: () => "default string", + }, +}); + +// This `render` helper function would typically be extracted and shared between +// test files. +const render = (renderedClient: ApolloClient) => + rtlRender( + + + + + + ); +``` + +Now let's write some tests 🎉 + +First, `createSchemaFetch` can be used to mock the global `fetch` implementation with one that resolves network requests with payloads generated from the test schema. + +```tsx title="products.test.tsx" +describe("Products", () => { + it("renders", async () => { + using _fetch = createSchemaFetch(schema).mockGlobal(); + + render(makeClient()); + + await screen.findByText("Loading..."); + + // title is rendering the default string scalar + const findAllByText = await screen.findAllByText(/default string/); + expect(findAllByText).toHaveLength(5); + + // the products resolver is returning 5 products + await screen.findByText(/0/); + await screen.findByText(/1/); + await screen.findByText(/2/); + await screen.findByText(/3/); + await screen.findByText(/4/); + }); +}); +``` + +#### A note on `using` and explicit resource management + +You may have noticed a new keyword in the first line of the test above: `using`. + +`using` is part of a [proposed new language feature](https://github.com/tc39/proposal-explicit-resource-management) which is currently at Stage 3 of the TC39 proposal process. + +If you are using TypeScript 5.2 or greater, or using Babel's [`@babel/plugin-proposal-explicit-resource-management` plugin](https://babeljs.io/docs/babel-plugin-proposal-explicit-resource-management), you can use the `using` keyword to automatically perform some cleanup when `_fetch` goes out of scope. In our case, this is when the test is complete; this means restoring the global fetch function to its original state automatically after each test. + +If your environment does not support explicit resource management, you'll find that calling `mockGlobal()` returns a restore function that you can manually call at the end of each test: + +```tsx title="products.test.tsx" +describe("Products", () => { + it("renders", async () => { + const { restore } = createSchemaFetch(schema).mockGlobal(); + + render(makeClient()); + + // make assertions against the rendered DOM output + + restore(); + }); +}); +``` + +## Modifying a test schema using `testSchema.add` and `testSchema.fork` + +If you need to make changes to the behavior of a schema after it has been created, you can use the `testSchema.add` method to add new resolvers to the schema or overwrite existing ones. +This can be useful for testing scenarios where the behavior of the schema needs to change inside a test. + +````tsx title="products.test.tsx" +describe("Products", () => { + it("renders", async () => { + const { restore } = createSchemaFetch(schema).mockGlobal(); + + render(makeClient()); + + // make assertions against the rendered DOM output + + // Here we want to change the return value of the `products` resolver + // for the next outgoing query. + testSchema.add({ + resolvers: { + Query: { + products: () => + Array.from({ length: 5 }, (_element, id) => ({ + // we want to return ids starting from 5 for the second request + id: id + 5, + mediaUrl: `https://example.com/image${id + 5}.jpg`, + })), + }, + }, + }); + + // trigger a new query with a user interaction + userEvent.click(screen.getByText("Fetch more")); + + // make assertions against the rendered DOM output + + restore(); + testSchema.reset(); + }); +}); +``` + +Alternatively, you can use `testSchema.fork` to create a new schema with the same configuration as the original schema, +but with the ability to make changes to the new isolated schema without affecting the original schema. +This can be useful if you just want to mock the global fetch function with a different schema for each test without +having to care about resetting your original testSchema. +You could also write incremental tests where each test builds on the previous one. + +If you use MSW, you will probably end up using `testSchema.add`, as MSW needs to be set up with a single schema for all tests. +If you are going the `createSchemaFetch` route, you can use `testSchema.fork` to create a new schema for each test, +and then use `forkedSchema.add` to make changes to the schema for that test. + +```tsx +const baseSchema = createTestSchema(staticSchema, { + resolvers: { + // ... + }, + scalars: { + // ... + }, +}); + +test("a test", () => { + const forkedSchema = baseSchema.fork(); + + const { restore } = createSchemaFetch(forkedSchema).mockGlobal(); + + // make assertions against the rendered DOM output + + forkedSchema.add({ + // ... + }); + + restore(); + // forkedSchema will just be discarded, and there is no need to reset it +}); +```` + +### FAQ + +#### When should I use `createSchemaFetch` vs [MSW](https://mswjs.io/)? + +There are many benefits to using [MSW](https://mswjs.io/): it's a powerful tool with a great set of APIs. Read more about its philosophy and benefits [here](https://mswjs.io/blog/why-mock-service-worker/). + +Wherever possible, use MSW: it enables more realistic tests that can catch more bugs by intercepting requests _after_ they've been dispatched by an application. MSW also supports both REST and GraphQL handlers, so if your application uses a combination (e.g. to fetch data from a third party endpoint), MSW will provide more flexibility than `createSchemaFetch`, which is a more lightweight solution. + +#### Should I share a single `ApolloClient` instance between tests? + +No; please create a new instance of `ApolloClient` for each test. Even if the cache is reset in between tests, the client maintains some internal state that is not reset. This could have some unintended consequences. For example, the `ApolloClient` instance could have pending queries that could cause the following test's queries to be deduplicated by default. + +Instead, create a `makeClient` function or equivalent so that every test uses the same client configuration as your production client, but no two tests share the same client instance. Here's an example: + + + +```ts title="src/client.ts" +import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client"; + +const httpLink = new HttpLink({ + uri: "https://example.com/graphql", +}); + +export const makeClient = () => { + return new ApolloClient({ + cache: new InMemoryCache(), + link: httpLink, + }); +}; + +export const client = makeClient(); +``` + + + +This way, every test can use `makeClient` to create a new client instance, and you can still use `client` in your production code. + +#### Can I use these testing tools with Vitest? + +Unfortunately not at the moment. This is caused by a known limitation with the `graphql` package and tools that bundle ESM by default known as the [dual package hazard](https://nodejs.org/api/packages.html#dual-package-hazard). + +Please see [this issue](https://github.com/graphql/graphql-js/issues/4062) to track the related discussion on the `graphql/graphql-js` repository. + +## Sandbox example + +For a working example that demonstrates how to use both Testing Library and Mock Service Worker to write integration tests with `createTestSchema`, check out this project on CodeSandbox: + +[![Edit Testing React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/apollographql/docs-examples/tree/main/apollo-client/v3/schema-driven-testing) diff --git a/docs/source/development-testing/static-typing.md b/docs/source/development-testing/static-typing.md index 3d11003c0c3..96a5148550c 100644 --- a/docs/source/development-testing/static-typing.md +++ b/docs/source/development-testing/static-typing.md @@ -16,7 +16,7 @@ Below, we'll guide you through installing and configuring GraphQL Code Generator To get started using GraphQL Code Generator, begin by installing the following packages (using Yarn or NPM): ```bash -yarn add -D typescript @graphql-codegen/cli @graphql-codegen/client-preset @graphql-typed-document-node/core +yarn add -D typescript graphql @graphql-codegen/cli @graphql-codegen/client-preset @graphql-typed-document-node/core ``` Next, we'll create a configuration file for GraphQL Code Generator, named [`codegen.ts`](https://www.the-guild.dev/graphql/codegen/docs/config-reference/codegen-config), at the root of our project: diff --git a/docs/source/development-testing/testing.mdx b/docs/source/development-testing/testing.mdx index 9821626749c..d32222cb6ab 100644 --- a/docs/source/development-testing/testing.mdx +++ b/docs/source/development-testing/testing.mdx @@ -101,7 +101,7 @@ Each mock object defines a `request` field (indicating the shape and variables o Alternatively, the `result` field can be a function that returns a mocked response after performing arbitrary logic: ```jsx -result: () => { +result: (variables) => { // `variables` is optional // ...arbitrary logic... return { @@ -150,6 +150,77 @@ it("renders without error", async () => { +#### Reusing mocks + +By default, a mock is only used once. If you want to reuse a mock for multiple operations, you can set the `maxUsageCount` field to a number indicating how many times the mock should be used: + + + +```jsx title="dog.test.js" +import { GET_DOG_QUERY } from "./dog"; + +const mocks = [ + { + request: { + query: GET_DOG_QUERY, + variables: { + name: "Buck" + } + }, + result: { + data: { + dog: { id: "1", name: "Buck", breed: "bulldog" } + } + }, + maxUsageCount: 2, // The mock can be used twice before it's removed, default is 1 + } +]; +``` + + + +Passing `Number.POSITIVE_INFINITY` will cause the mock to be reused indefinitely. + +### Dynamic variables + +Sometimes, the exact value of the variables being passed are not known. The `MockedResponse` object takes a `variableMatcher` property that is a function that takes the variables and returns a boolean indication if this mock should match the invocation for the provided query. You cannot specify this parameter and `request.variables` at the same time. + +For example, this mock will match all dog queries: + +```ts +import { MockedResponse } from "@apollo/client/testing"; + +const dogMock: MockedResponse = { + request: { + query: GET_DOG_QUERY + }, + variableMatcher: (variables) => true, + result: { + data: { dog: { id: 1, name: 'Buck', breed: 'poodle' } }, + }, +}; +``` + +This can also be useful for asserting specific variables individually: + +```ts +import { MockedResponse } from "@apollo/client/testing"; + +const dogMock: MockedResponse = { + request: { + query: GET_DOG_QUERY + }, + variableMatcher: jest.fn().mockReturnValue(true), + result: { + data: { dog: { id: 1, name: 'Buck', breed: 'poodle' } }, + }, +}; + +expect(variableMatcher).toHaveBeenCalledWith(expect.objectContaining({ + name: 'Buck' +})); +``` + ### Setting `addTypename` In the example above, we set the `addTypename` prop of `MockedProvider` to `false`. This prevents Apollo Client from automatically adding the special `__typename` field to every object it queries for (it does this by default to support data normalization in the cache). diff --git a/docs/source/get-started.mdx b/docs/source/get-started.mdx index 72442e4450c..0dc91bc6281 100644 --- a/docs/source/get-started.mdx +++ b/docs/source/get-started.mdx @@ -10,7 +10,7 @@ Hello! 👋 This short tutorial gets you up and running with Apollo Client. To start this tutorial, do one of the following: -- Create a new React project locally with [Create React App](https://create-react-app.dev/), or +- Create a new React project locally with [Vite](https://vitejs.dev/), or - Create a new React sandbox on [CodeSandbox](https://codesandbox.io/). ## Step 2: Install dependencies @@ -34,15 +34,15 @@ Our example application will use the [FlyBy GraphQL API](https://flyby-router-de With our dependencies set up, we can now initialize an `ApolloClient` instance. -In `index.js`, let's first import the symbols we need from `@apollo/client`: +In `main.jsx`, let's first import the symbols we need from `@apollo/client`: -```js title="index.js" +```jsx title="main.jsx" import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client'; ``` Next we'll initialize `ApolloClient`, passing its constructor a configuration object with the `uri` and `cache` fields: -```js title="index.js" +```jsx title="main.jsx" const client = new ApolloClient({ uri: 'https://flyby-router-demo.herokuapp.com/', cache: new InMemoryCache(), @@ -54,9 +54,9 @@ const client = new ApolloClient({ That's it! Our `client` is ready to start fetching data. Now before we start using Apollo Client with React, let's first try sending a query with plain JavaScript. -In the same `index.js` file, call `client.query()` with the query string (wrapped in the `gql` template literal) shown below: +In the same `main.jsx` file, call `client.query()` with the query string (wrapped in the `gql` template literal) shown below: -```js title="index.js" +```jsx title="main.jsx" // const client = ... client @@ -85,9 +85,9 @@ Let's look at how that works! You connect Apollo Client to React with the `ApolloProvider` component. Similar to React's [`Context.Provider`](https://react.dev/reference/react/useContext), `ApolloProvider` wraps your React app and places Apollo Client on the context, enabling you to access it from anywhere in your component tree. -In `index.js`, let's wrap our React app with an `ApolloProvider`. We suggest putting the `ApolloProvider` somewhere high in your app, above any component that might need to access GraphQL data. +In `main.jsx`, let's wrap our React app with an `ApolloProvider`. We suggest putting the `ApolloProvider` somewhere high in your app, above any component that might need to access GraphQL data. -```jsx title="index.js" {15-17} +```jsx title="main.jsx" {15-17} import React from 'react'; import * as ReactDOM from 'react-dom/client'; import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; @@ -112,9 +112,9 @@ root.render( After your `ApolloProvider` is hooked up, you can start requesting data with `useQuery`. The `useQuery` hook is a [React hook](https://react.dev/reference/react) that shares GraphQL data with your UI. -Switching over to our `App.js` file, we'll start by replacing our existing file contents with the code snippet below: +Switching over to our `App.jsx` file, we'll start by replacing our existing file contents with the code snippet below: -```js title="App.js" +```jsx title="App.jsx" // Import everything needed to use the `useQuery` hook import { useQuery, gql } from '@apollo/client'; @@ -129,7 +129,7 @@ export default function App() { We can define the query we want to execute by wrapping it in the `gql` template literal: -```js title="App.js" +```jsx title="App.jsx" const GET_LOCATIONS = gql` query GetLocations { locations { @@ -144,7 +144,7 @@ const GET_LOCATIONS = gql` Next, let's define a component named `DisplayLocations` that executes our `GET_LOCATIONS` query with the `useQuery` hook: -```js title="App.js" {2} +```jsx title="App.jsx" {2} function DisplayLocations() { const { loading, error, data } = useQuery(GET_LOCATIONS); @@ -171,7 +171,7 @@ Whenever this component renders, the `useQuery` hook automatically executes our Finally, we'll add `DisplayLocations` to our existing component tree: -```jsx title="App.js" {6} +```jsx title="App.jsx" {6} export default function App() { return (
diff --git a/docs/source/img/spotify-playlists.jpg b/docs/source/img/spotify-playlists.jpg new file mode 100644 index 00000000000..2c2584c38bd Binary files /dev/null and b/docs/source/img/spotify-playlists.jpg differ diff --git a/docs/source/index.mdx b/docs/source/index.mdx index 4d4d59f2e68..6c6565888e1 100644 --- a/docs/source/index.mdx +++ b/docs/source/index.mdx @@ -4,29 +4,46 @@ title: Introduction to Apollo Client import { Link } from 'gatsby'; -**Apollo Client** is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI. +**Apollo Client** is a comprehensive state management library for JavaScript. It enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI. Apollo Client helps you structure code in an economical, predictable, and declarative way that's consistent with modern development practices. The core `@apollo/client` library provides built-in integration with React, and the larger Apollo community maintains [integrations for other popular view layers](#community-integrations).

Get started!

-## Features +## Core features + +Some of Apollo Client's core capabilities include: - **Declarative data fetching:** Write a query and receive data without manually tracking loading states. +- **Normalized request and response caching:** Boost performance by responding almost immediately to queries with cached data. - **Excellent developer experience:** Enjoy helpful tooling for TypeScript, Chrome / Firefox devtools, and VS Code. -- **Designed for modern React:** Take advantage of the latest React features, such as hooks. -- **Incrementally adoptable:** Drop Apollo into any JavaScript app and incorporate it feature by feature. +- **Designed for modern React:** Take advantage of the latest React features, such as hooks and Suspense. +- **Incrementally adoptable:** Drop Apollo Client into any JavaScript app and incorporate it feature by feature. - **Universally compatible:** Use any build setup and any GraphQL API. - **Community driven:** Share knowledge with thousands of developers in the GraphQL community. +## GraphOS supported features + +Apollo Client works seamlessly with these GraphOS router supported features: + +- Receiving data for specific fields incrementally with the [`@defer` directive](/graphos/operations/defer) +- Real-time updates via [GraphQL subscriptions](/graphos/operations/subscriptions) +- Safelisting with [persisted queries](/graphos/operations/persisted-queries) + + + +Apollo Client also supports `@defer` and GraphQL subscription implementations outside of GraphOS. + + + ## Recommended docs After you [get started](./get-started/), check out the full Apollo Client documentation in the navigation on the left. diff --git a/docs/source/integrations/integrations.md b/docs/source/integrations/integrations.md index 237baf515e2..aefa3193e73 100644 --- a/docs/source/integrations/integrations.md +++ b/docs/source/integrations/integrations.md @@ -12,7 +12,7 @@ Apollo Client, unlike some other tools in the React ecosystem, requires _no_ com ## Vue -A [Vue.js](https://vuejs.org/) integration is maintained by Guillaume Chau ([@Akryum](https://github.com/Akryum)). See the Github [repository](https://github.com/Akryum/vue-apollo) for more details. +A [Vue.js](https://vuejs.org/) integration is maintained by Guillaume Chau ([@Akryum](https://github.com/Akryum)). See the Github [repository](https://github.com/vuejs/apollo) for more details. ## Svelte diff --git a/docs/source/integrations/react-native.md b/docs/source/integrations/react-native.md index eabd1ec6076..3990bdffde4 100644 --- a/docs/source/integrations/react-native.md +++ b/docs/source/integrations/react-native.md @@ -17,7 +17,7 @@ import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; // Initialize Apollo Client const client = new ApolloClient({ - uri: 'localhost:4000/graphql', + uri: 'http://localhost:4000/graphql', cache: new InMemoryCache() }); @@ -38,11 +38,34 @@ For more information on setting up Apollo Client, see [Getting started](../get-s ## Apollo Client Devtools -[React Native Debugger](https://github.com/jhen0409/react-native-debugger) supports the [Apollo Client Devtools](../development-testing/developer-tooling/#apollo-client-devtools): +#### 1. Using [React Native Debugger](https://github.com/jhen0409/react-native-debugger) -1. Install React Native Debugger and open it. -2. Enable "Debug JS Remotely" in your app. -3. If you don't see the Developer Tools panel or the Apollo tab is missing from it, toggle the Developer Tools by right-clicking anywhere and selecting **Toggle Developer Tools**. +The React Native Debugger supports the [Apollo Client Devtools](../development-testing/developer-tooling/#apollo-client-devtools): + + 1. Install React Native Debugger and open it. + 2. Enable "Debug JS Remotely" in your app. + 3. If you don't see the Developer Tools panel or the Apollo tab is missing from it, toggle the Developer Tools by right-clicking anywhere and selecting **Toggle Developer Tools**. + +#### 2. Using [Flipper](https://fbflipper.com/) + +A community plugin called [React Native Apollo devtools](https://github.com/razorpay/react-native-apollo-devtools) is available for Flipper, which supports viewing cache data. + + 1. Install Flipper and open it. + 2. Go to add plugin and search for `react-native-apollo-devtools` and install it + 3. Add `react-native-flipper` and `react-native-apollo-devtools-client` as dev dependecy to react native app. + 4. Initialize the plugin with flipper on client side + + ```ts + import { apolloDevToolsInit } from 'react-native-apollo-devtools-client'; + + const client = new ApolloClient({ + // ... + }); + + if (__DEV__) { + apolloDevToolsInit(client); + } + ``` ## Consuming multipart HTTP via text streaming diff --git a/docs/source/performance/babel.md b/docs/source/performance/babel.md index 6b6b5911dc0..3fa6cde18d1 100644 --- a/docs/source/performance/babel.md +++ b/docs/source/performance/babel.md @@ -8,7 +8,7 @@ To avoid this runtime overhead, you can precompile your queries created with `gr 1. Using [babel-plugin-graphql-tag](#using-babel-plugin-graphql-tag) 2. Using [graphql-tag.macro](#using-graphql-tagmacro) -1. Using [ts-transform-graphql-tag](#using-ts-transform-graphql-tag) for Typescript +1. Using [ts-transform-graphql-tag](#using-ts-transform-graphql-tag) for TypeScript If you prefer to keep your GraphQL code in separate files (`.graphql` or `.gql`) you can use [babel-plugin-import-graphql](https://github.com/detrohutt/babel-plugin-import-graphql). This plugin still uses `graphql-tag` under the hood, but transparently. You simply `import` your operations/fragments as if each were an export from your GraphQL file. This carries the same precompilation benefits as the above approaches. diff --git a/docs/source/performance/optimistic-ui.mdx b/docs/source/performance/optimistic-ui.mdx index 35332dce9e2..b4c85efd82f 100644 --- a/docs/source/performance/optimistic-ui.mdx +++ b/docs/source/performance/optimistic-ui.mdx @@ -73,6 +73,55 @@ As this example shows, the value of `optimisticResponse` is an object that match 5. Apollo Client notifies all affected queries again. The associated components re-render, but if the server's response matches our `optimisticResponse`, this is invisible to the user. + + +## Bailing out of an optimistic update + + + +In some cases you may want to skip an optimistic update. For example, you may want to perform an optimistic update _only_ when certain variables are passed to the mutation. To skip an update, pass a function to the `optimisticResponse` option and return the `IGNORE` sentinel object available on the second argument to bail out of the optimistic update. + +Consider this example: + +```tsx +const UPDATE_COMMENT = gql` + mutation UpdateComment($commentId: ID!, $commentContent: String!) { + updateComment(commentId: $commentId, content: $commentContent) { + id + __typename + content + } + } +`; + +function CommentPageWithData() { + const [mutate] = useMutation(UPDATE_COMMENT); + + return ( + + mutate({ + variables: { commentId, commentContent }, + optimisticResponse: (vars, { IGNORE }) => { + if (commentContent === "foo") { + // conditionally bail out of optimistic updates + return IGNORE; + } + return { + updateComment: { + id: commentId, + __typename: "Comment", + content: commentContent + } + } + }, + }) + } + /> + ); +} +``` + ## Example: Adding a new object to a list The previous example shows how to provide an optimistic result for an object that's _already_ in the Apollo Client cache. But what about a mutation that creates a _new_ object? This works similarly. diff --git a/eslint-local-rules/require-using-disposable.ts b/eslint-local-rules/require-using-disposable.ts index 35489c166a3..efeb5cce7b6 100644 --- a/eslint-local-rules/require-using-disposable.ts +++ b/eslint-local-rules/require-using-disposable.ts @@ -55,9 +55,9 @@ export const rule = ESLintUtils.RuleCreator.withoutDocs({ }); function parts(type: ts.Type): ts.Type[] { - return type.isUnion() - ? utils.unionTypeParts(type).flatMap(parts) - : type.isIntersection() - ? utils.intersectionTypeParts(type).flatMap(parts) - : [type]; + return ( + type.isUnion() ? utils.unionTypeParts(type).flatMap(parts) + : type.isIntersection() ? utils.intersectionTypeParts(type).flatMap(parts) + : [type] + ); } diff --git a/integration-tests/browser-esm/html/jspm-prepared.html b/integration-tests/browser-esm/html/jspm-prepared.html index 7395ff614af..88dd8e6a545 100644 --- a/integration-tests/browser-esm/html/jspm-prepared.html +++ b/integration-tests/browser-esm/html/jspm-prepared.html @@ -8,12 +8,11 @@ }, "scopes": { "https://ga.jspm.io/": { - "@wry/context": "https://ga.jspm.io/npm:@wry/context@0.7.3/lib/index.js", "@wry/equality": "https://ga.jspm.io/npm:@wry/equality@0.5.6/lib/index.js", "@wry/trie": "https://ga.jspm.io/npm:@wry/trie@0.4.3/lib/index.js", "graphql": "https://ga.jspm.io/npm:graphql@16.6.0/index.mjs", "graphql-tag": "https://ga.jspm.io/npm:graphql-tag@2.12.6/lib/index.js", - "optimism": "https://ga.jspm.io/npm:optimism@0.17.5/lib/index.js", + "optimism": "https://ga.jspm.io/npm:optimism@0.18.0/lib/index.js", "react": "https://ga.jspm.io/npm:react@18.2.0/dev.index.js", "symbol-observable": "https://ga.jspm.io/npm:symbol-observable@4.0.0/lib/index.js", "ts-invariant": "https://ga.jspm.io/npm:ts-invariant@0.10.3/lib/invariant.js", diff --git a/integration-tests/browser-esm/html/unpkg-unmangled.html b/integration-tests/browser-esm/html/unpkg-unmangled.html index 0e7de49a86b..535ed1282fe 100644 --- a/integration-tests/browser-esm/html/unpkg-unmangled.html +++ b/integration-tests/browser-esm/html/unpkg-unmangled.html @@ -5,12 +5,11 @@ { "imports": { "@apollo/client": "https://unpkg.com/@apollo/client@0.0.0-pr-10915-20230616125401/index.js", - "@wry/context": "https://unpkg.com/@wry/context@0.7.3/lib/index.js", "@wry/equality": "https://unpkg.com/@wry/equality@0.5.6/lib/index.js", "@wry/trie": "https://unpkg.com/@wry/trie@0.4.3/lib/index.js", "graphql": "https://unpkg.com/graphql@16.6.0/index.mjs", "graphql-tag": "https://unpkg.com/graphql-tag@2.12.6/lib/index.js", - "optimism": "https://unpkg.com/optimism@0.17.5/lib/index.js", + "optimism": "https://unpkg.com/optimism@0.18.0/lib/index.js", "react": "https://ga.jspm.io/npm:react@18.2.0/dev.index.js", "symbol-observable": "https://ga.jspm.io/npm:symbol-observable@4.0.0/lib/index.js", "ts-invariant": "https://unpkg.com/ts-invariant@0.10.3/lib/invariant.js", diff --git a/integration-tests/cra4/package.json b/integration-tests/cra4/package.json index c322dd2a70f..5f7d6210643 100644 --- a/integration-tests/cra4/package.json +++ b/integration-tests/cra4/package.json @@ -3,11 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { - "@apollo/client": "^3.7.16", + "@apollo/client": "^3.10.1", "graphql": "^16.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-redux": "^8.0.5", + "react": "^18.3.0", + "react-dom": "^18.3.0", "react-scripts": "^4" }, "scripts": { @@ -35,12 +34,12 @@ ] }, "devDependencies": { + "@playwright/test": "*", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", - "typescript": "^4.9.4", - "shared": "*", "playwright": "*", - "@playwright/test": "*", - "serve": "*" + "serve": "*", + "shared": "*", + "typescript": "^4.9.4" } } diff --git a/integration-tests/cra4/src/App.tsx b/integration-tests/cra4/src/App.tsx index cf87849afbd..189ee26af73 100644 --- a/integration-tests/cra4/src/App.tsx +++ b/integration-tests/cra4/src/App.tsx @@ -55,9 +55,9 @@ export default function App() { function Main() { const { data } = useQuery(QUERY); - return data ? ( -
    {data?.products.map(({ id, title }) =>
  • {title}
  • )}
- ) : ( - <>loading - ); + return data ? +
    + {data?.products.map(({ id, title }) =>
  • {title}
  • )} +
+ : <>loading; } diff --git a/integration-tests/cra5/package.json b/integration-tests/cra5/package.json index b56bd784086..f03ab7a73bd 100644 --- a/integration-tests/cra5/package.json +++ b/integration-tests/cra5/package.json @@ -3,14 +3,14 @@ "version": "0.1.0", "private": true, "dependencies": { + "@apollo/client": "^3.10.1", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "graphql": "^16.8.1", + "react": "^18.3.0", + "react-dom": "^18.3.0", "react-scripts": "5.0.1", - "typescript": "^4.9.5", - "@apollo/client": "^3.7.16", - "graphql": "^16.8.1" + "typescript": "^4.9.5" }, "scripts": { "start": "PORT=3000 react-scripts start", @@ -37,9 +37,9 @@ ] }, "devDependencies": { - "shared": "*", - "playwright": "*", "@playwright/test": "*", - "serve": "*" + "playwright": "*", + "serve": "*", + "shared": "*" } } diff --git a/integration-tests/cra5/src/App.tsx b/integration-tests/cra5/src/App.tsx index cf87849afbd..189ee26af73 100644 --- a/integration-tests/cra5/src/App.tsx +++ b/integration-tests/cra5/src/App.tsx @@ -55,9 +55,9 @@ export default function App() { function Main() { const { data } = useQuery(QUERY); - return data ? ( -
    {data?.products.map(({ id, title }) =>
  • {title}
  • )}
- ) : ( - <>loading - ); + return data ? +
    + {data?.products.map(({ id, title }) =>
  • {title}
  • )} +
+ : <>loading; } diff --git a/integration-tests/next/package.json b/integration-tests/next/package.json index 9ec7bcdb050..5272499fc27 100644 --- a/integration-tests/next/package.json +++ b/integration-tests/next/package.json @@ -7,12 +7,12 @@ "build": "next build", "start": "next start", "serve-app": "npm run start", - "ci-preparations": "npm install @apollo/experimental-nextjs-app-support", + "ci-preparations": "npm install --legacy-peer-deps @apollo/experimental-nextjs-app-support", "test": "playwright test" }, "dependencies": { - "@apollo/client": "^3.8.0-beta.4", - "@apollo/experimental-nextjs-app-support": "^0.3.1", + "@apollo/client": "^3.10.1", + "@apollo/experimental-nextjs-app-support": "^0.10.0", "@graphql-tools/schema": "^10.0.0", "@types/node": "20.3.1", "@types/react": "18.2.14", @@ -20,9 +20,9 @@ "deepmerge": "^4.3.1", "graphql": "^16.8.1", "lodash": "^4.17.21", - "next": "13.4.7", - "react": "18.2.0", - "react-dom": "18.2.0", + "next": "^14.2.3", + "react": "^18.3.0", + "react-dom": "^18.3.0", "typescript": "5.1.3" }, "devDependencies": { diff --git a/integration-tests/next/src/app/cc/ApolloWrapper.tsx b/integration-tests/next/src/app/cc/ApolloWrapper.tsx index f5430c2371d..0b6d14ea8ff 100644 --- a/integration-tests/next/src/app/cc/ApolloWrapper.tsx +++ b/integration-tests/next/src/app/cc/ApolloWrapper.tsx @@ -1,6 +1,6 @@ "use client"; -import React from "react"; -import { HttpLink } from "@apollo/client"; +import * as React from "react"; +import { ApolloLink, HttpLink } from "@apollo/client"; import { ApolloNextAppProvider, NextSSRInMemoryCache, @@ -9,7 +9,7 @@ import { import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev"; import { setVerbosity } from "ts-invariant"; -import { schemaLink } from "@/libs/schemaLink"; +import { schemaLink } from "@/libs/schemaLink.ts"; //if (process.env.NODE_ENV === 'development') { setVerbosity("debug"); @@ -31,7 +31,10 @@ export function ApolloWrapper({ children }: React.PropsWithChildren<{}>) { return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), - link: typeof window === "undefined" ? schemaLink : httpLink, + link: + typeof window === "undefined" ? + (schemaLink as ApolloLink) + : (httpLink as ApolloLink), }); } } diff --git a/integration-tests/next/src/app/cc/layout.tsx b/integration-tests/next/src/app/cc/layout.tsx index bc8642b3a3d..720fc4c4e44 100644 --- a/integration-tests/next/src/app/cc/layout.tsx +++ b/integration-tests/next/src/app/cc/layout.tsx @@ -1,4 +1,4 @@ -import { ApolloWrapper } from "./ApolloWrapper"; +import { ApolloWrapper } from "./ApolloWrapper.tsx"; export default async function Layout({ children, diff --git a/integration-tests/next/src/app/client.ts b/integration-tests/next/src/app/client.ts index b43496b26de..857603e3566 100644 --- a/integration-tests/next/src/app/client.ts +++ b/integration-tests/next/src/app/client.ts @@ -1,4 +1,4 @@ -import { schemaLink } from "@/libs/schemaLink"; +import { schemaLink } from "@/libs/schemaLink.ts"; import { ApolloClient, InMemoryCache } from "@apollo/client"; import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; diff --git a/integration-tests/next/src/app/page.tsx b/integration-tests/next/src/app/page.tsx index 96bd69a7efa..204869c5cfd 100644 --- a/integration-tests/next/src/app/page.tsx +++ b/integration-tests/next/src/app/page.tsx @@ -1,6 +1,6 @@ import type { TypedDocumentNode } from "@apollo/client"; import { gql } from "@apollo/client"; -import { getClient } from "./client"; +import { getClient } from "./client.ts"; const QUERY: TypedDocumentNode<{ products: { diff --git a/integration-tests/next/src/libs/apolloClient.ts b/integration-tests/next/src/libs/apolloClient.ts index 96a17171142..ac47b293b57 100644 --- a/integration-tests/next/src/libs/apolloClient.ts +++ b/integration-tests/next/src/libs/apolloClient.ts @@ -1,4 +1,4 @@ -import { useRef } from "react"; +import * as React from "react"; import type { NormalizedCacheObject } from "@apollo/client"; import { ApolloClient, @@ -12,7 +12,7 @@ import { onError } from "@apollo/client/link/error"; import merge from "deepmerge"; import isEqual from "lodash/isEqual"; import type { GetServerSidePropsResult } from "next"; -import { schemaLink } from "./schemaLink"; +import { schemaLink } from "./schemaLink.ts"; export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__"; @@ -48,7 +48,9 @@ function createApolloClient() { link: from([ errorLink, delayLink, - typeof window === "undefined" ? schemaLink : httpLink, + typeof window === "undefined" ? + (schemaLink as ApolloLink) + : (httpLink as ApolloLink), ]), cache: new InMemoryCache(), }); @@ -103,7 +105,7 @@ export function addApolloState( export function useApollo(pageProps?: ApolloProps) { const state = pageProps?.[APOLLO_STATE_PROP_NAME]; - const storeRef = useRef>(); + const storeRef = React.useRef>(); if (!storeRef.current) { storeRef.current = initializeApollo(state); } diff --git a/integration-tests/next/src/pages/_app.tsx b/integration-tests/next/src/pages/_app.tsx index 48c3fa2e958..8589ee6b07b 100644 --- a/integration-tests/next/src/pages/_app.tsx +++ b/integration-tests/next/src/pages/_app.tsx @@ -1,5 +1,5 @@ import { ApolloProvider } from "@apollo/client"; -import { useApollo } from "../libs/apolloClient"; +import { useApollo } from "../libs/apolloClient.ts"; import type { AppProps } from "next/app"; export default function App({ Component, pageProps }: AppProps) { diff --git a/integration-tests/next/src/pages/pages.tsx b/integration-tests/next/src/pages/pages.tsx index 9bbc5e035dc..5c8fecd6d85 100644 --- a/integration-tests/next/src/pages/pages.tsx +++ b/integration-tests/next/src/pages/pages.tsx @@ -3,7 +3,7 @@ import type { TypedDocumentNode } from "@apollo/client"; import { gql, useQuery } from "@apollo/client"; import type { GetStaticProps } from "next"; -import { addApolloState, initializeApollo } from "@/libs/apolloClient"; +import { addApolloState, initializeApollo } from "@/libs/apolloClient.ts"; const QUERY: TypedDocumentNode<{ products: { diff --git a/integration-tests/next/tsconfig.json b/integration-tests/next/tsconfig.json index 0c7555fa765..e20adc4ca61 100644 --- a/integration-tests/next/tsconfig.json +++ b/integration-tests/next/tsconfig.json @@ -14,6 +14,7 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, + "allowImportingTsExtensions": true, "plugins": [ { "name": "next" diff --git a/integration-tests/node-esm/package.json b/integration-tests/node-esm/package.json index 2af5e60112b..aa520583989 100644 --- a/integration-tests/node-esm/package.json +++ b/integration-tests/node-esm/package.json @@ -8,9 +8,9 @@ "test": "node test-cjs.cjs && node test-esm.mjs" }, "dependencies": { - "@apollo/client": "^3.7.16", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "resolve-esm": "^1.4.0" diff --git a/integration-tests/node-standard/package.json b/integration-tests/node-standard/package.json index b7e472ada86..1746711014a 100644 --- a/integration-tests/node-standard/package.json +++ b/integration-tests/node-standard/package.json @@ -7,9 +7,9 @@ "test": "node test-cjs.js && node test-esm.mjs" }, "dependencies": { - "@apollo/client": "^3.7.16", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "resolve-esm": "^1.4.0" diff --git a/integration-tests/package-lock.json b/integration-tests/package-lock.json index 63aa27c80d2..d82c91f6131 100644 --- a/integration-tests/package-lock.json +++ b/integration-tests/package-lock.json @@ -32,11 +32,10 @@ "cra4": { "version": "0.1.0", "dependencies": { - "@apollo/client": "^3.7.16", + "@apollo/client": "^3.10.1", "graphql": "^16.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-redux": "^8.0.5", + "react": "^18.3.0", + "react-dom": "^18.3.0", "react-scripts": "^4" }, "devDependencies": { @@ -49,6 +48,60 @@ "typescript": "^4.9.4" } }, + "cra4/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "cra4/node_modules/@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "cra4/node_modules/@babel/core/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "cra4/node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "cra4/node_modules/@cnakazawa/watch": { "version": "1.0.4", "license": "Apache-2.0", @@ -92,19 +145,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "cra4/node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "cra4/node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "license": "MIT", @@ -112,20 +152,6 @@ "node": ">= 4" } }, - "cra4/node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "cra4/node_modules/@gar/promisify": { - "version": "1.1.3", - "license": "MIT" - }, "cra4/node_modules/@hapi/address": { "version": "2.1.4", "license": "BSD-3-Clause" @@ -312,19 +338,6 @@ "node-notifier": "^8.0.0" } }, - "cra4/node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, "cra4/node_modules/@jest/reporters/node_modules/jest-resolve": { "version": "26.6.2", "license": "MIT", @@ -354,14 +367,6 @@ "node": ">= 10.13.0" } }, - "cra4/node_modules/@jest/reporters/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "cra4/node_modules/@jest/source-map": { "version": "26.6.2", "license": "MIT", @@ -439,63 +444,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/@npmcli/fs": { - "version": "1.1.1", - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "cra4/node_modules/@npmcli/fs/node_modules/lru-cache": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/@npmcli/fs/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, - "cra4/node_modules/@npmcli/move-file": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "cra4/node_modules/@rollup/plugin-node-resolve": { "version": "7.1.3", "license": "MIT", @@ -544,14 +492,6 @@ "@types/node": "*" } }, - "cra4/node_modules/@types/hoist-non-react-statics": { - "version": "3.3.1", - "license": "MIT", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, "cra4/node_modules/@types/html-minifier-terser": { "version": "5.1.2", "license": "MIT" @@ -586,10 +526,6 @@ "source-map": "^0.6.1" } }, - "cra4/node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "license": "MIT" - }, "cra4/node_modules/@types/webpack": { "version": "4.41.33", "license": "MIT", @@ -619,8 +555,9 @@ } }, "cra4/node_modules/@types/yargs": { - "version": "15.0.15", - "license": "MIT", + "version": "15.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", + "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", "dependencies": { "@types/yargs-parser": "*" } @@ -666,9 +603,9 @@ } }, "cra4/node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -679,10 +616,6 @@ "node": ">=10" } }, - "cra4/node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, "cra4/node_modules/@typescript-eslint/experimental-utils": { "version": "4.33.0", "license": "MIT", @@ -792,9 +725,9 @@ } }, "cra4/node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -805,10 +738,6 @@ "node": ">=10" } }, - "cra4/node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, "cra4/node_modules/@typescript-eslint/visitor-keys": { "version": "4.33.0", "license": "MIT", @@ -991,17 +920,6 @@ "node": ">=8.9" } }, - "cra4/node_modules/aggregate-error": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "cra4/node_modules/ajv": { "version": "6.12.6", "license": "MIT", @@ -1034,13 +952,6 @@ "version": "1.0.2", "license": "MIT" }, - "cra4/node_modules/ansi-colors": { - "version": "4.1.3", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "cra4/node_modules/ansi-html": { "version": "0.0.7", "engines": [ @@ -1143,24 +1054,6 @@ "util": "0.10.3" } }, - "cra4/node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "license": "ISC" - }, - "cra4/node_modules/assert/node_modules/util": { - "version": "0.10.3", - "license": "MIT", - "dependencies": { - "inherits": "2.0.1" - } - }, - "cra4/node_modules/assign-symbols": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/astral-regex": { "version": "2.0.0", "license": "MIT", @@ -1421,6 +1314,14 @@ ], "license": "MIT" }, + "cra4/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "engines": { + "node": ">=0.10.0" + } + }, "cra4/node_modules/bindings": { "version": "1.5.0", "license": "MIT", @@ -1429,10 +1330,6 @@ "file-uri-to-path": "1.0.0" } }, - "cra4/node_modules/bn.js": { - "version": "5.2.1", - "license": "MIT" - }, "cra4/node_modules/bonjour": { "version": "3.5.0", "license": "MIT", @@ -1460,6 +1357,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "cra4/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, "cra4/node_modules/brorand": { "version": "1.1.0", "license": "MIT" @@ -1476,6 +1412,25 @@ "safe-buffer": "^5.0.1" } }, + "cra4/node_modules/browserify-aes/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/browserify-cipher": { "version": "1.0.1", "license": "MIT", @@ -1495,22 +1450,41 @@ "safe-buffer": "^5.1.2" } }, - "cra4/node_modules/browserify-rsa": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "cra4/node_modules/browserify-sign": { - "version": "4.2.1", - "license": "ISC", - "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", + "cra4/node_modules/browserify-des/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "cra4/node_modules/browserify-rsa": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "cra4/node_modules/browserify-sign": { + "version": "4.2.1", + "license": "ISC", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", "elliptic": "^6.5.3", "inherits": "^2.0.4", "parse-asn1": "^5.1.5", @@ -1543,6 +1517,27 @@ "pako": "~1.0.5" } }, + "cra4/node_modules/browserslist": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", + "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", + "dependencies": { + "caniuse-lite": "^1.0.30001125", + "electron-to-chromium": "^1.3.564", + "escalade": "^3.0.2", + "node-releases": "^1.1.61" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + }, "cra4/node_modules/buffer-indexof": { "version": "1.1.1", "license": "MIT" @@ -1555,57 +1550,6 @@ "version": "3.0.0", "license": "MIT" }, - "cra4/node_modules/cacache": { - "version": "15.3.0", - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "cra4/node_modules/cacache/node_modules/lru-cache": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/cacache/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, "cra4/node_modules/cache-base": { "version": "1.0.1", "license": "MIT", @@ -1624,33 +1568,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/caller-callsite": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/caller-callsite/node_modules/callsites": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/caller-path": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/camelcase": { "version": "6.3.0", "license": "MIT", @@ -1693,11 +1610,9 @@ } }, "cra4/node_modules/chownr": { - "version": "2.0.0", - "license": "ISC", - "engines": { - "node": ">=10" - } + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "cra4/node_modules/ci-info": { "version": "2.0.0", @@ -1711,6 +1626,25 @@ "safe-buffer": "^5.0.1" } }, + "cra4/node_modules/cipher-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/cjs-module-lexer": { "version": "0.6.0", "license": "MIT" @@ -1738,63 +1672,16 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "cra4/node_modules/clean-css": { @@ -1807,11 +1694,14 @@ "node": ">= 4.0" } }, - "cra4/node_modules/clean-stack": { - "version": "2.2.0", - "license": "MIT", - "engines": { - "node": ">=6" + "cra4/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, "cra4/node_modules/collection-visit": { @@ -1833,6 +1723,11 @@ "color-string": "^1.6.0" } }, + "cra4/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, "cra4/node_modules/color-string": { "version": "1.9.1", "license": "MIT", @@ -1848,10 +1743,6 @@ "color-name": "1.1.3" } }, - "cra4/node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, "cra4/node_modules/commander": { "version": "4.1.1", "license": "MIT", @@ -1883,10 +1774,6 @@ "typedarray": "^0.0.6" } }, - "cra4/node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/concat-stream/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -1900,13 +1787,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/connect-history-api-fallback": { "version": "1.6.0", "license": "MIT", @@ -1921,6 +1801,14 @@ "version": "1.0.0", "license": "MIT" }, + "cra4/node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, "cra4/node_modules/copy-concurrently": { "version": "1.0.5", "license": "ISC", @@ -1950,6 +1838,44 @@ "node": ">=0.10.0" } }, + "cra4/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "cra4/node_modules/cosmiconfig/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "cra4/node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, "cra4/node_modules/create-ecdh": { "version": "4.0.4", "license": "MIT", @@ -1985,11 +1911,30 @@ "sha.js": "^2.4.8" } }, - "cra4/node_modules/crypto-browserify": { - "version": "3.12.0", - "license": "MIT", - "dependencies": { - "browserify-cipher": "^1.0.0", + "cra4/node_modules/create-hmac/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "cra4/node_modules/crypto-browserify": { + "version": "3.12.0", + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", "create-ecdh": "^4.0.0", "create-hash": "^1.1.0", @@ -2217,60 +2162,10 @@ "node": ">=6.9.0" } }, - "cra4/node_modules/cssnano/node_modules/cosmiconfig": { - "version": "5.2.1", - "license": "MIT", - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/cssnano/node_modules/import-fresh": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/cssnano/node_modules/parse-json": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/cssnano/node_modules/resolve-from": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/cyclist": { "version": "1.0.2", "license": "MIT" }, - "cra4/node_modules/d": { - "version": "1.0.1", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "cra4/node_modules/debug": { "version": "4.3.4", "license": "MIT", @@ -2368,13 +2263,6 @@ "node": ">=4" } }, - "cra4/node_modules/default-gateway/node_modules/path-key": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/default-gateway/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -2383,23 +2271,6 @@ "semver": "bin/semver" } }, - "cra4/node_modules/default-gateway/node_modules/shebang-command": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/default-gateway/node_modules/shebang-regex": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/default-gateway/node_modules/which": { "version": "1.3.1", "license": "ISC", @@ -2410,17 +2281,6 @@ "which": "bin/which" } }, - "cra4/node_modules/define-property": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/del": { "version": "4.1.1", "license": "MIT", @@ -2493,6 +2353,14 @@ "minimalistic-assert": "^1.0.0" } }, + "cra4/node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "engines": { + "node": ">= 10.14.2" + } + }, "cra4/node_modules/diffie-hellman": { "version": "5.0.3", "license": "MIT", @@ -2514,6 +2382,25 @@ "safe-buffer": "^5.0.1" } }, + "cra4/node_modules/dns-packet/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/dns-txt": { "version": "2.0.2", "license": "MIT", @@ -2539,13 +2426,6 @@ "node": ">=8" } }, - "cra4/node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "cra4/node_modules/dotenv": { "version": "8.2.0", "license": "BSD-2-Clause", @@ -2563,10 +2443,6 @@ "stream-shift": "^1.0.0" } }, - "cra4/node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/duplexify/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -2580,13 +2456,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/ejs": { "version": "2.7.4", "hasInstallScript": true, @@ -2622,12 +2491,10 @@ "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "cra4/node_modules/end-of-stream": { - "version": "1.4.4", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } + "cra4/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "cra4/node_modules/enhanced-resolve": { "version": "4.5.0", @@ -2640,10 +2507,6 @@ "node": ">=6.9.0" } }, - "cra4/node_modules/enhanced-resolve/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/enhanced-resolve/node_modules/memory-fs": { "version": "0.5.0", "license": "MIT", @@ -2668,13 +2531,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/enhanced-resolve/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/enquirer": { "version": "2.3.6", "license": "MIT", @@ -2685,46 +2541,6 @@ "node": ">=8.6" } }, - "cra4/node_modules/errno": { - "version": "0.1.8", - "license": "MIT", - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "cra4/node_modules/es5-ext": { - "version": "0.10.62", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "cra4/node_modules/es6-iterator": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "cra4/node_modules/es6-symbol": { - "version": "3.1.3", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "cra4/node_modules/escape-string-regexp": { "version": "1.0.5", "license": "MIT", @@ -2957,40 +2773,6 @@ "node": ">=4" } }, - "cra4/node_modules/eslint-scope": { - "version": "5.1.1", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "cra4/node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "cra4/node_modules/eslint-utils": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, "cra4/node_modules/eslint-visitor-keys": { "version": "2.1.0", "license": "Apache-2.0", @@ -3037,13 +2819,6 @@ "url": "https://opencollective.com/webpack" } }, - "cra4/node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, "cra4/node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "license": "MIT", @@ -3074,19 +2849,6 @@ "node": ">=4" } }, - "cra4/node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "cra4/node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "license": "MIT", @@ -3094,16 +2856,6 @@ "node": ">= 4" } }, - "cra4/node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "cra4/node_modules/espree": { "version": "7.3.1", "license": "BSD-2-Clause", @@ -3123,6 +2875,19 @@ "node": ">=4" } }, + "cra4/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "cra4/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, "cra4/node_modules/eventsource": { "version": "2.0.2", "license": "MIT", @@ -3138,6 +2903,25 @@ "safe-buffer": "^5.1.1" } }, + "cra4/node_modules/evp_bytestokey/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/exec-sh": { "version": "0.3.6", "license": "MIT" @@ -3206,56 +2990,16 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "cra4/node_modules/expand-brackets/node_modules/is-extendable": { @@ -3265,13 +3009,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/expand-brackets/node_modules/ms": { "version": "2.0.0", "license": "MIT" @@ -3291,81 +3028,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/expect/node_modules/diff-sequences": { - "version": "26.6.2", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/expect/node_modules/jest-diff": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/expect/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/expect/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/expect/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "cra4/node_modules/ext": { - "version": "1.7.0", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, - "cra4/node_modules/ext/node_modules/type": { - "version": "2.7.2", - "license": "ISC" - }, - "cra4/node_modules/extend-shallow": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/extglob": { "version": "2.0.4", "license": "MIT", @@ -3460,6 +3122,39 @@ "node": ">= 0.4.0" } }, + "cra4/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, "cra4/node_modules/find-cache-dir": { "version": "2.1.0", "license": "MIT", @@ -3495,10 +3190,6 @@ "readable-stream": "^2.3.6" } }, - "cra4/node_modules/flush-write-stream/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/flush-write-stream/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -3512,13 +3203,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/flush-write-stream/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/for-in": { "version": "1.0.2", "license": "MIT", @@ -3536,130 +3220,40 @@ "minimatch": "^3.0.4", "semver": "^5.6.0", "tapable": "^1.0.0", - "worker-rpc": "^0.1.0" - }, - "engines": { - "node": ">=6.11.5", - "yarn": ">=1.0.0" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "2.4.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "worker-rpc": "^0.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.11.5", + "yarn": ">=1.0.0" } }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", + "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "3.2.1", "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "color-convert": "^1.9.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", "engines": { "node": ">=4" } }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": { - "version": "3.0.0", + "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "2.4.2", "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", + "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "1.9.3", "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "color-name": "1.1.3" } }, "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": { @@ -3702,17 +3296,6 @@ "node": ">=4" } }, - "cra4/node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/fragment-cache": { "version": "0.2.1", "license": "MIT", @@ -3731,10 +3314,6 @@ "readable-stream": "^2.0.0" } }, - "cra4/node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/from2/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -3748,13 +3327,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/fs-extra": { "version": "9.1.0", "license": "MIT", @@ -3768,16 +3340,6 @@ "node": ">=10" } }, - "cra4/node_modules/fs-minipass": { - "version": "2.1.0", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "cra4/node_modules/fs-write-stream-atomic": { "version": "1.0.10", "license": "ISC", @@ -3788,10 +3350,6 @@ "readable-stream": "1 || 2" } }, - "cra4/node_modules/fs-write-stream-atomic/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/fs-write-stream-atomic/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -3805,13 +3363,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/fs-write-stream-atomic/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/functional-red-black-tree": { "version": "1.0.1", "license": "MIT" @@ -3829,13 +3380,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "cra4/node_modules/get-value": { - "version": "2.0.6", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/glob-parent": { "version": "5.1.2", "license": "ISC", @@ -3846,6 +3390,20 @@ "node": ">= 6" } }, + "cra4/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "cra4/node_modules/growly": { "version": "1.3.0", "license": "MIT", @@ -3862,55 +3420,18 @@ "node": ">=6" } }, - "cra4/node_modules/has-value": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/has-values": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/has-values/node_modules/is-number": { + "cra4/node_modules/has-flag": { "version": "3.0.0", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "cra4/node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, + "cra4/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", "engines": { "node": ">=0.10.0" } @@ -4057,85 +3578,6 @@ "node": ">=4.0.0" } }, - "cra4/node_modules/http-proxy-middleware/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/is-number": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/http-proxy-middleware/node_modules/micromatch": { "version": "3.1.10", "license": "MIT", @@ -4158,17 +3600,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/http-proxy-middleware/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/https-browserify": { "version": "1.0.0", "license": "MIT" @@ -4212,6 +3643,15 @@ "version": "0.1.5", "license": "MIT" }, + "cra4/node_modules/immer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", + "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "cra4/node_modules/import-cwd": { "version": "2.1.0", "license": "MIT", @@ -4232,28 +3672,10 @@ "node": ">=4" } }, - "cra4/node_modules/import-from/node_modules/resolve-from": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/indent-string": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "cra4/node_modules/indexes-of": { "version": "1.0.1", "license": "MIT" }, - "cra4/node_modules/infer-owner": { - "version": "1.0.4", - "license": "ISC" - }, "cra4/node_modules/internal-ip": { "version": "4.3.0", "license": "MIT", @@ -4283,13 +3705,6 @@ "node": ">= 0.10" } }, - "cra4/node_modules/is-absolute-url": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/is-accessor-descriptor": { "version": "1.0.0", "license": "MIT", @@ -4310,14 +3725,26 @@ "engines": { "node": ">= 0.4" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "cra4/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "cra4/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "cra4/node_modules/is-buffer": { - "version": "1.1.6", - "license": "MIT" - }, "cra4/node_modules/is-ci": { "version": "2.0.0", "license": "MIT", @@ -4369,16 +3796,36 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/is-extendable": { - "version": "1.0.1", - "license": "MIT", + "cra4/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", "dependencies": { - "is-plain-object": "^2.0.4" + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, + "cra4/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, "cra4/node_modules/is-path-cwd": { "version": "2.2.0", "license": "MIT", @@ -4413,16 +3860,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/is-plain-object": { - "version": "2.0.4", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/is-resolvable": { "version": "1.1.0", "license": "ISC" @@ -4434,11 +3871,31 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/isobject": { - "version": "3.0.1", - "license": "MIT", + "cra4/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "cra4/node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + } + }, + "cra4/node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" } }, "cra4/node_modules/jest": { @@ -4498,59 +3955,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-circus/node_modules/diff-sequences": { - "version": "26.6.2", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-circus/node_modules/jest-diff": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-circus/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-circus/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-cli": { "version": "26.6.3", "license": "MIT", @@ -4576,69 +3980,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-cli/node_modules/camelcase": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "cra4/node_modules/jest-cli/node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "cra4/node_modules/jest-cli/node_modules/wrap-ansi": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/jest-cli/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, - "cra4/node_modules/jest-cli/node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/jest-cli/node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "cra4/node_modules/jest-config": { "version": "26.6.3", "license": "MIT", @@ -4674,13 +4015,6 @@ } } }, - "cra4/node_modules/jest-config/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, "cra4/node_modules/jest-config/node_modules/jest-resolve": { "version": "26.6.2", "license": "MIT", @@ -4698,17 +4032,18 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-config/node_modules/pretty-format": { + "cra4/node_modules/jest-diff": { "version": "26.6.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "engines": { - "node": ">= 10" + "node": ">= 10.14.2" } }, "cra4/node_modules/jest-docblock": { @@ -4735,26 +4070,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-each/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-each/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-environment-jsdom": { "version": "26.6.2", "license": "MIT", @@ -4786,6 +4101,14 @@ "node": ">= 10.14.2" } }, + "cra4/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "engines": { + "node": ">= 10.14.2" + } + }, "cra4/node_modules/jest-haste-map": { "version": "26.6.2", "license": "MIT", @@ -4850,88 +4173,29 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-jasmine2/node_modules/diff-sequences": { - "version": "26.6.2", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-jasmine2/node_modules/jest-diff": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-jasmine2/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-jasmine2/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-leak-detector": { "version": "26.6.2", "license": "MIT", "dependencies": { "jest-get-type": "^26.3.0", "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", + }, "engines": { "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-leak-detector/node_modules/pretty-format": { + "cra4/node_modules/jest-matcher-utils": { "version": "26.6.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" }, "engines": { - "node": ">= 10" + "node": ">= 10.14.2" } }, "cra4/node_modules/jest-message-util": { @@ -4952,19 +4216,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-message-util/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-mock": { "version": "26.6.2", "license": "MIT", @@ -5109,22 +4360,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-runtime/node_modules/camelcase": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "cra4/node_modules/jest-runtime/node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, "cra4/node_modules/jest-runtime/node_modules/jest-resolve": { "version": "26.6.2", "license": "MIT", @@ -5142,53 +4377,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-runtime/node_modules/wrap-ansi": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/jest-runtime/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, - "cra4/node_modules/jest-runtime/node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/jest-runtime/node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "cra4/node_modules/jest-serializer": { "version": "26.6.2", "license": "MIT", @@ -5225,46 +4413,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "26.6.2", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, "cra4/node_modules/jest-snapshot/node_modules/jest-resolve": { "version": "26.6.2", "license": "MIT", @@ -5282,19 +4430,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-util": { "version": "26.6.2", "license": "MIT", @@ -5325,26 +4460,6 @@ "node": ">= 10.14.2" } }, - "cra4/node_modules/jest-validate/node_modules/jest-get-type": { - "version": "26.3.0", - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, - "cra4/node_modules/jest-validate/node_modules/pretty-format": { - "version": "26.6.2", - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, "cra4/node_modules/jest-watch-typeahead": { "version": "0.6.1", "license": "MIT", @@ -5407,6 +4522,19 @@ "node": ">=4.3.0 <5.0.0 || >=5.10" } }, + "cra4/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "cra4/node_modules/locate-path": { "version": "5.0.0", "license": "MIT", @@ -5496,37 +4624,24 @@ "safe-buffer": "^5.1.2" } }, - "cra4/node_modules/memory-fs": { - "version": "0.4.1", - "license": "MIT", - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "cra4/node_modules/memory-fs/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, - "cra4/node_modules/memory-fs/node_modules/readable-stream": { - "version": "2.3.8", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "cra4/node_modules/memory-fs/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } + "cra4/node_modules/md5.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "cra4/node_modules/microevent.ts": { "version": "0.1.1", @@ -5547,6 +4662,17 @@ "version": "4.12.0", "license": "MIT" }, + "cra4/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "cra4/node_modules/mini-css-extract-plugin": { "version": "0.11.3", "license": "MIT", @@ -5567,103 +4693,55 @@ "webpack": "^4.4.0 || ^5.0.0" } }, - "cra4/node_modules/mini-css-extract-plugin/node_modules/json5": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "cra4/node_modules/mini-css-extract-plugin/node_modules/loader-utils": { - "version": "1.4.2", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "cra4/node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "cra4/node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "license": "MIT" - }, - "cra4/node_modules/minipass": { - "version": "3.3.6", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/minipass-collect": { + "cra4/node_modules/mini-css-extract-plugin/node_modules/json5": { "version": "1.0.2", - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "minimist": "^1.2.0" }, - "engines": { - "node": ">= 8" + "bin": { + "json5": "lib/cli.js" } }, - "cra4/node_modules/minipass-flush": { - "version": "1.0.5", - "license": "ISC", + "cra4/node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "1.4.2", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": ">= 8" + "node": ">=4.0.0" } }, - "cra4/node_modules/minipass-pipeline": { - "version": "1.2.4", - "license": "ISC", + "cra4/node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "cra4/node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" + "cra4/node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "license": "MIT" }, - "cra4/node_modules/minizlib": { - "version": "2.1.2", - "license": "MIT", + "cra4/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 8" + "node": "*" } }, - "cra4/node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, "cra4/node_modules/mississippi": { "version": "3.0.0", "license": "BSD-2-Clause", @@ -5767,10 +4845,6 @@ "querystring": "^0.2.0" } }, - "cra4/node_modules/next-tick": { - "version": "1.1.0", - "license": "ISC" - }, "cra4/node_modules/nice-try": { "version": "1.0.5", "license": "MIT" @@ -5824,10 +4898,6 @@ "version": "2.0.3", "license": "ISC" }, - "cra4/node_modules/node-libs-browser/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/node-libs-browser/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -5841,13 +4911,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/node-libs-browser/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/node-libs-browser/node_modules/util": { "version": "0.11.1", "license": "MIT", @@ -5868,6 +4931,11 @@ "which": "^2.0.2" } }, + "cra4/node_modules/node-releases": { + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==" + }, "cra4/node_modules/normalize-package-data": { "version": "2.5.0", "license": "BSD-2-Clause", @@ -5925,43 +4993,16 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "cra4/node_modules/object-copy/node_modules/kind-of": { @@ -6094,19 +5135,6 @@ "node": ">=8" } }, - "cra4/node_modules/p-map": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "cra4/node_modules/p-retry": { "version": "3.0.1", "license": "MIT", @@ -6130,10 +5158,6 @@ "readable-stream": "^2.1.5" } }, - "cra4/node_modules/parallel-transform/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/parallel-transform/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -6147,13 +5171,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/parallel-transform/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/parse-asn1": { "version": "5.1.6", "license": "ISC", @@ -6165,6 +5182,25 @@ "safe-buffer": "^5.1.1" } }, + "cra4/node_modules/parse-asn1/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/pascalcase": { "version": "0.1.1", "license": "MIT", @@ -6180,6 +5216,14 @@ "version": "1.0.2", "license": "MIT" }, + "cra4/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, "cra4/node_modules/pbkdf2": { "version": "3.1.2", "license": "MIT", @@ -6194,6 +5238,25 @@ "node": ">=0.12" } }, + "cra4/node_modules/pbkdf2/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "cra4/node_modules/picocolors": { "version": "0.2.1", "license": "ISC" @@ -6307,11 +5370,13 @@ } }, "cra4/node_modules/postcss": { - "version": "7.0.39", - "license": "MIT", + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" }, "engines": { "node": ">=6.0.0" @@ -6679,48 +5744,6 @@ "url": "https://opencollective.com/postcss/" } }, - "cra4/node_modules/postcss-load-config/node_modules/cosmiconfig": { - "version": "5.2.1", - "license": "MIT", - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/postcss-load-config/node_modules/import-fresh": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/postcss-load-config/node_modules/parse-json": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/postcss-load-config/node_modules/resolve-from": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/postcss-loader": { "version": "3.0.0", "license": "MIT", @@ -7300,7 +6323,9 @@ "license": "ISC" }, "cra4/node_modules/postcss-safe-parser/node_modules/postcss": { - "version": "8.4.24", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -7315,11 +6340,10 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -7350,35 +6374,89 @@ "svgo": "^1.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.9.0" + } + }, + "cra4/node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "license": "MIT" + }, + "cra4/node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "cra4/node_modules/postcss-values-parser": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "cra4/node_modules/postcss/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "cra4/node_modules/postcss/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "cra4/node_modules/postcss/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "cra4/node_modules/postcss-svgo/node_modules/postcss-value-parser": { - "version": "3.3.1", - "license": "MIT" - }, - "cra4/node_modules/postcss-unique-selectors": { - "version": "4.0.1", - "license": "MIT", + "cra4/node_modules/postcss/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" + "color-name": "1.1.3" } }, - "cra4/node_modules/postcss-values-parser": { - "version": "2.0.1", - "license": "MIT", + "cra4/node_modules/postcss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dependencies": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=6.14.4" + "node": ">=6" } }, "cra4/node_modules/prepend-http": { @@ -7396,6 +6474,20 @@ "renderkid": "^2.0.4" } }, + "cra4/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, "cra4/node_modules/process": { "version": "0.11.10", "license": "MIT", @@ -7410,10 +6502,6 @@ "node": ">=0.4.0" } }, - "cra4/node_modules/promise-inflight": { - "version": "1.0.1", - "license": "ISC" - }, "cra4/node_modules/prompts": { "version": "2.4.0", "license": "MIT", @@ -7425,10 +6513,6 @@ "node": ">= 6" } }, - "cra4/node_modules/prr": { - "version": "1.0.1", - "license": "MIT" - }, "cra4/node_modules/public-encrypt": { "version": "4.0.3", "license": "MIT", @@ -7445,13 +6529,24 @@ "version": "4.12.0", "license": "MIT" }, - "cra4/node_modules/pump": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "cra4/node_modules/public-encrypt/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "cra4/node_modules/pumpify": { "version": "1.5.1", @@ -7515,6 +6610,33 @@ "safe-buffer": "^5.1.0" } }, + "cra4/node_modules/randomfill/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "cra4/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, "cra4/node_modules/react-app-polyfill": { "version": "2.0.0", "license": "MIT", @@ -7580,26 +6702,6 @@ "node": ">=4" } }, - "cra4/node_modules/react-dev-utils/node_modules/browserslist": { - "version": "4.14.2", - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001125", - "electron-to-chromium": "^1.3.564", - "escalade": "^3.0.2", - "node-releases": "^1.1.61" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - }, "cra4/node_modules/react-dev-utils/node_modules/chalk": { "version": "2.4.2", "license": "MIT", @@ -7626,10 +6728,6 @@ "color-name": "1.1.3" } }, - "cra4/node_modules/react-dev-utils/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, "cra4/node_modules/react-dev-utils/node_modules/escape-string-regexp": { "version": "2.0.0", "license": "MIT", @@ -7655,37 +6753,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "cra4/node_modules/react-dev-utils/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/react-dev-utils/node_modules/immer": { - "version": "8.0.1", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "cra4/node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "cra4/node_modules/react-dev-utils/node_modules/node-releases": { - "version": "1.1.77", - "license": "MIT" - }, "cra4/node_modules/react-dev-utils/node_modules/strip-ansi": { "version": "6.0.0", "license": "MIT", @@ -7710,47 +6777,6 @@ "version": "17.0.2", "license": "MIT" }, - "cra4/node_modules/react-redux": { - "version": "8.1.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "cra4/node_modules/react-redux/node_modules/react-is": { - "version": "18.2.0", - "license": "MIT" - }, "cra4/node_modules/react-refresh": { "version": "0.8.3", "license": "MIT", @@ -7840,43 +6866,6 @@ } } }, - "cra4/node_modules/react-scripts/node_modules/@babel/core": { - "version": "7.12.3", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.1", - "@babel/parser": "^7.12.3", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "cra4/node_modules/react-scripts/node_modules/@babel/core/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, "cra4/node_modules/react-scripts/node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.4.3", "license": "MIT", @@ -7922,18 +6911,25 @@ } } }, - "cra4/node_modules/react-scripts/node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "cra4/node_modules/react-scripts/node_modules/source-map": { "version": "0.7.4", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "engines": { "node": ">= 8" } }, - "cra4/node_modules/react-scripts/node_modules/source-map": { - "version": "0.5.7", - "license": "BSD-3-Clause", + "cra4/node_modules/react-scripts/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "cra4/node_modules/read-pkg": { @@ -7978,6 +6974,56 @@ "node": ">=8" } }, + "cra4/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "cra4/node_modules/readdirp/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/readdirp/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "cra4/node_modules/recursive-readdir": { "version": "2.2.2", "license": "MIT", @@ -7988,16 +7034,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.0.4", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "cra4/node_modules/regex-not": { "version": "1.0.2", "license": "MIT", @@ -8080,6 +7116,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "cra4/node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "cra4/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "engines": { + "node": ">=4" + } + }, "cra4/node_modules/resolve-url": { "version": "0.2.1", "license": "MIT" @@ -8103,16 +7158,6 @@ "node": ">=6.0.0" } }, - "cra4/node_modules/resolve-url-loader/node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/resolve-url-loader/node_modules/camelcase": { "version": "5.3.1", "license": "MIT", @@ -8120,53 +7165,6 @@ "node": ">=6" } }, - "cra4/node_modules/resolve-url-loader/node_modules/chalk": { - "version": "2.4.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/resolve-url-loader/node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/resolve-url-loader/node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "cra4/node_modules/resolve-url-loader/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, - "cra4/node_modules/resolve-url-loader/node_modules/convert-source-map": { - "version": "1.7.0", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "cra4/node_modules/resolve-url-loader/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/resolve-url-loader/node_modules/json5": { "version": "1.0.2", "license": "MIT", @@ -8189,32 +7187,6 @@ "node": ">=4.0.0" } }, - "cra4/node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.36", - "license": "MIT", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "cra4/node_modules/resolve-url-loader/node_modules/supports-color": { - "version": "6.1.0", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "cra4/node_modules/ret": { "version": "0.1.15", "license": "MIT", @@ -8298,13 +7270,6 @@ "rollup": ">=0.66.0 <3" } }, - "cra4/node_modules/rollup-plugin-terser/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/rollup-plugin-terser/node_modules/jest-worker": { "version": "24.9.0", "license": "MIT", @@ -8340,10 +7305,6 @@ "estree-walker": "^0.6.1" } }, - "cra4/node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "license": "MIT" - }, "cra4/node_modules/rsvp": { "version": "4.8.5", "license": "MIT", @@ -8394,35 +7355,6 @@ "normalize-path": "^2.1.1" } }, - "cra4/node_modules/sane/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/sane/node_modules/cross-spawn": { "version": "6.0.5", "license": "MIT", @@ -8453,29 +7385,6 @@ "node": ">=6" } }, - "cra4/node_modules/sane/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/sane/node_modules/get-stream": { "version": "4.1.0", "license": "MIT", @@ -8486,33 +7395,6 @@ "node": ">=6" } }, - "cra4/node_modules/sane/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/is-number": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/sane/node_modules/is-stream": { "version": "1.1.0", "license": "MIT", @@ -8562,13 +7444,6 @@ "node": ">=4" } }, - "cra4/node_modules/sane/node_modules/path-key": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/sane/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -8577,34 +7452,6 @@ "semver": "bin/semver" } }, - "cra4/node_modules/sane/node_modules/shebang-command": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/shebang-regex": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/sane/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/sane/node_modules/which": { "version": "1.3.1", "license": "ISC", @@ -8620,8 +7467,9 @@ "license": "CC0-1.0" }, "cra4/node_modules/sass-loader": { - "version": "10.4.1", - "license": "MIT", + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", + "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", "dependencies": { "klona": "^2.0.4", "loader-utils": "^2.0.0", @@ -8638,7 +7486,7 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "webpack": "^4.36.0 || ^5.0.0" }, @@ -8744,19 +7592,57 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/setimmediate": { - "version": "1.0.5", - "license": "MIT" - }, - "cra4/node_modules/sha.js": { - "version": "2.4.11", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" + "cra4/node_modules/setimmediate": { + "version": "1.0.5", + "license": "MIT" + }, + "cra4/node_modules/sha.js": { + "version": "2.4.11", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "cra4/node_modules/sha.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "cra4/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "cra4/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" } }, "cra4/node_modules/shell-quote": { @@ -8775,10 +7661,6 @@ "is-arrayish": "^0.3.1" } }, - "cra4/node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "license": "MIT" - }, "cra4/node_modules/slice-ansi": { "version": "4.0.0", "license": "MIT", @@ -8880,56 +7762,16 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "cra4/node_modules/snapdragon/node_modules/is-extendable": { @@ -8939,13 +7781,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/snapdragon/node_modules/ms": { "version": "2.0.0", "license": "MIT" @@ -9048,13 +7883,11 @@ } }, "cra4/node_modules/ssri": { - "version": "8.0.1", - "license": "ISC", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" + "figgy-pudding": "^3.5.1" } }, "cra4/node_modules/static-extend": { @@ -9078,63 +7911,16 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "cra4/node_modules/stream-browserify": { @@ -9145,10 +7931,6 @@ "readable-stream": "^2.0.2" } }, - "cra4/node_modules/stream-browserify/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/stream-browserify/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -9162,13 +7944,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/stream-browserify/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/stream-each": { "version": "1.2.3", "license": "MIT", @@ -9188,10 +7963,6 @@ "xtend": "^4.0.0" } }, - "cra4/node_modules/stream-http/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/stream-http/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -9205,13 +7976,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/stream-http/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/stream-shift": { "version": "1.0.1", "license": "MIT" @@ -9235,10 +7999,6 @@ "node": ">=8" } }, - "cra4/node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, "cra4/node_modules/strip-ansi": { "version": "6.0.1", "license": "MIT", @@ -9358,42 +8118,6 @@ "node": ">=6" } }, - "cra4/node_modules/tar": { - "version": "6.1.15", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "cra4/node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "cra4/node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, "cra4/node_modules/temp-dir": { "version": "1.0.0", "license": "MIT", @@ -9461,8 +8185,9 @@ } }, "cra4/node_modules/terser-webpack-plugin/node_modules/acorn": { - "version": "8.9.0", - "license": "MIT", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -9562,8 +8287,9 @@ } }, "cra4/node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.18.1", - "license": "BSD-2-Clause", + "version": "5.30.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz", + "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -9593,10 +8319,6 @@ "xtend": "~4.0.1" } }, - "cra4/node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/through2/node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -9610,13 +8332,6 @@ "util-deprecate": "~1.0.1" } }, - "cra4/node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/timers-browserify": { "version": "2.0.12", "license": "MIT", @@ -9668,6 +8383,18 @@ "node": ">=0.10.0" } }, + "cra4/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "cra4/node_modules/ts-pnp": { "version": "1.2.0", "license": "MIT", @@ -9684,9 +8411,16 @@ "version": "0.0.0", "license": "MIT" }, - "cra4/node_modules/type": { - "version": "1.2.0", - "license": "ISC" + "cra4/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "cra4/node_modules/typedarray": { "version": "0.0.6", @@ -9720,20 +8454,6 @@ "version": "2.0.0", "license": "MIT" }, - "cra4/node_modules/unique-filename": { - "version": "1.1.1", - "license": "ISC", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "cra4/node_modules/unique-slug": { - "version": "2.0.2", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, "cra4/node_modules/unique-string": { "version": "1.0.0", "license": "MIT", @@ -9777,17 +8497,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/unset-value/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, "cra4/node_modules/urix": { "version": "0.1.0", "license": "MIT" @@ -9848,13 +8557,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/use-sync-external-store": { - "version": "1.2.0", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "cra4/node_modules/util.promisify": { "version": "1.0.0", "license": "MIT", @@ -9914,72 +8616,33 @@ "neo-async": "^2.5.0" }, "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" - } - }, - "cra4/node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "license": "MIT", - "optional": true, - "dependencies": { - "chokidar": "^2.1.8" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/anymatch": { - "version": "2.0.0", - "license": "ISC", - "optional": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "license": "MIT", - "optional": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/binary-extensions": { - "version": "1.13.1", + "cra4/node_modules/watchpack-chokidar2": { + "version": "2.0.1", "license": "MIT", "optional": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "chokidar": "^2.1.8" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", + "cra4/node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "license": "ISC", "optional": true, "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", + "cra4/node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", "license": "MIT", "optional": true, "dependencies": { - "is-extendable": "^0.1.0" + "remove-trailing-separator": "^1.0.1" }, "engines": { "node": ">=0.10.0" @@ -10006,31 +8669,6 @@ "fsevents": "^1.2.7" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "optional": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/watchpack-chokidar2/node_modules/fsevents": { "version": "1.2.13", "hasInstallScript": true, @@ -10067,52 +8705,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/is-binary-path": { - "version": "1.0.1", - "license": "MIT", - "optional": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/is-number": { - "version": "3.0.0", - "license": "MIT", - "optional": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "optional": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT", - "optional": true - }, "cra4/node_modules/watchpack-chokidar2/node_modules/micromatch": { "version": "3.1.10", "license": "MIT", @@ -10136,53 +8728,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/watchpack-chokidar2/node_modules/readable-stream": { - "version": "2.3.8", - "license": "MIT", - "optional": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/readdirp": { - "version": "2.2.1", - "license": "MIT", - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "cra4/node_modules/watchpack-chokidar2/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "optional": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack": { "version": "4.44.2", "license": "MIT", @@ -10247,23 +8792,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "cra4/node_modules/webpack-dev-middleware/node_modules/mime": { - "version": "2.6.0", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "cra4/node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "cra4/node_modules/webpack-dev-server": { "version": "3.11.1", "license": "MIT", @@ -10352,42 +8880,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/webpack-dev-server/node_modules/binary-extensions": { - "version": "1.13.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/camelcase": { "version": "5.3.1", "license": "MIT", @@ -10448,37 +8940,10 @@ "color-name": "1.1.3" } }, - "cra4/node_modules/webpack-dev-server/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, "cra4/node_modules/webpack-dev-server/node_modules/emoji-regex": { "version": "7.0.3", "license": "MIT" }, - "cra4/node_modules/webpack-dev-server/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/find-up": { "version": "3.0.0", "license": "MIT", @@ -10523,13 +8988,6 @@ "node": ">=0.10.0" } }, - "cra4/node_modules/webpack-dev-server/node_modules/has-flag": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/import-local": { "version": "2.0.0", "license": "MIT", @@ -10551,53 +9009,12 @@ "node": ">=8" } }, - "cra4/node_modules/webpack-dev-server/node_modules/is-binary-path": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { "version": "2.0.0", "license": "MIT", "engines": { "node": ">=4" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/is-number": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" + } }, "cra4/node_modules/webpack-dev-server/node_modules/locate-path": { "version": "3.0.0", @@ -10649,48 +9066,6 @@ "node": ">=4" } }, - "cra4/node_modules/webpack-dev-server/node_modules/readable-stream": { - "version": "2.3.8", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "2.2.1", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/resolve-cwd": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/resolve-from": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "1.0.0", "license": "MIT", @@ -10711,13 +9086,6 @@ "semver": "bin/semver.js" } }, - "cra4/node_modules/webpack-dev-server/node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/string-width": { "version": "3.1.0", "license": "MIT", @@ -10767,17 +9135,6 @@ "node": ">=6" } }, - "cra4/node_modules/webpack-dev-server/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack-dev-server/node_modules/wrap-ansi": { "version": "5.1.0", "license": "MIT", @@ -10807,17 +9164,6 @@ "node": ">=6" } }, - "cra4/node_modules/webpack-dev-server/node_modules/ws": { - "version": "6.2.2", - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "cra4/node_modules/webpack-dev-server/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, "cra4/node_modules/webpack-dev-server/node_modules/yargs": { "version": "13.3.2", "license": "MIT", @@ -10927,35 +9273,6 @@ "node": ">=0.4.0" } }, - "cra4/node_modules/webpack/node_modules/braces": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack/node_modules/cacache": { "version": "12.0.4", "license": "ISC", @@ -10977,10 +9294,6 @@ "y18n": "^4.0.0" } }, - "cra4/node_modules/webpack/node_modules/chownr": { - "version": "1.1.4", - "license": "ISC" - }, "cra4/node_modules/webpack/node_modules/eslint-scope": { "version": "4.0.3", "license": "BSD-2-Clause", @@ -10992,63 +9305,6 @@ "node": ">=4.0.0" } }, - "cra4/node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "cra4/node_modules/webpack/node_modules/fill-range": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/is-extendable": { - "version": "0.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/is-number": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "cra4/node_modules/webpack/node_modules/is-wsl": { "version": "1.1.0", "license": "MIT", @@ -11129,13 +9385,6 @@ "randombytes": "^2.1.0" } }, - "cra4/node_modules/webpack/node_modules/ssri": { - "version": "6.0.2", - "license": "ISC", - "dependencies": { - "figgy-pudding": "^3.5.1" - } - }, "cra4/node_modules/webpack/node_modules/terser-webpack-plugin": { "version": "1.4.5", "license": "MIT", @@ -11157,21 +9406,6 @@ "webpack": "^4.0.0" } }, - "cra4/node_modules/webpack/node_modules/to-regex-range": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "cra4/node_modules/webpack/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, "cra4/node_modules/which-module": { "version": "2.0.1", "license": "ISC" @@ -11383,6 +9617,27 @@ "microevent.ts": "~0.1.1" } }, + "cra4/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "cra4/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "cra4/node_modules/xtend": { "version": "4.0.2", "license": "MIT", @@ -11390,15 +9645,66 @@ "node": ">=0.4" } }, + "cra4/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "cra4/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "cra4/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "cra4/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "cra4/node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, "cra5": { "version": "0.1.0", "dependencies": { - "@apollo/client": "^3.7.16", + "@apollo/client": "^3.10.1", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "graphql": "^16.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0", "react-scripts": "5.0.1", "typescript": "^4.9.5" }, @@ -11421,8 +9727,8 @@ "next": { "version": "0.1.0", "dependencies": { - "@apollo/client": "^3.8.0-beta.4", - "@apollo/experimental-nextjs-app-support": "^0.3.1", + "@apollo/client": "^3.10.1", + "@apollo/experimental-nextjs-app-support": "^0.10.0", "@graphql-tools/schema": "^10.0.0", "@types/node": "20.3.1", "@types/react": "18.2.14", @@ -11430,9 +9736,9 @@ "deepmerge": "^4.3.1", "graphql": "^16.8.1", "lodash": "^4.17.21", - "next": "13.4.7", - "react": "18.2.0", - "react-dom": "18.2.0", + "next": "^14.2.3", + "react": "^18.3.0", + "react-dom": "^18.3.0", "typescript": "5.1.3" }, "devDependencies": { @@ -11442,95 +9748,74 @@ "shared": "*" } }, - "next/node_modules/@apollo/client": { - "version": "3.8.0-beta.4", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.8.0-beta.4.tgz", - "integrity": "sha512-UtRjX1PXjHl/Sg+cVAyyiCR8y33knGu2PC3AwJo1AUCnfgFix5R6lG5LOH418qaLE/HtNjsrn28yd1LSYdVrQg==", + "next/node_modules/@apollo/experimental-nextjs-app-support": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@apollo/experimental-nextjs-app-support/-/experimental-nextjs-app-support-0.10.0.tgz", + "integrity": "sha512-S3mfZRnAAAaKwA8RNckS4TWYLX5utpmRTwG3WGFtpooYx8QQG8xft0p0a9eTQ53Jrw3nSMJc/wOOsT/5noMCQg==", "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "@wry/context": "^0.7.3", - "@wry/equality": "^0.5.6", - "@wry/trie": "^0.4.3", - "graphql-tag": "^2.12.6", - "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.17.4", - "prop-types": "^15.7.2", - "response-iterator": "^0.2.6", - "symbol-observable": "^4.0.0", - "ts-invariant": "^0.10.3", - "tslib": "^2.3.0", - "zen-observable-ts": "^1.2.5" + "@apollo/client-react-streaming": "0.10.0" }, "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", - "graphql-ws": "^5.5.5", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" - }, - "peerDependenciesMeta": { - "graphql-ws": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "subscriptions-transport-ws": { - "optional": true - } + "@apollo/client": "^3.9.6", + "next": "^13.4.1 || ^14.0.0", + "react": "^18" } - }, - "next/node_modules/@apollo/experimental-nextjs-app-support": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apollo/experimental-nextjs-app-support/-/experimental-nextjs-app-support-0.3.1.tgz", - "integrity": "sha512-LvFSNWd8hs7AqAgARc5Kv7NftIIFX8X2MW/BKohnuLJAZce8+2GrTGl7QlNrmyF5pac2rSPQzaWMcYpBM+/1Mw==", + }, + "next/node_modules/@apollo/experimental-nextjs-app-support/node_modules/@apollo/client-react-streaming": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@apollo/client-react-streaming/-/client-react-streaming-0.10.0.tgz", + "integrity": "sha512-iZ2jYghRS71xFv6O3Js5Ojrrmk4SnIEKwPRKIswQyAtqjHrfvUTyXCDzxrhPcGQe/y7su/XcE7Xp0kOp7yTnlg==", "dependencies": { - "superjson": "^1.12.2", + "superjson": "^1.12.2 || ^2.0.0", "ts-invariant": "^0.10.3" }, "peerDependencies": { - "@apollo/client": ">=3.8.0-beta.4", - "next": "^13.4.1", + "@apollo/client": "^3.9.6", "react": "^18" } }, + "next/node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, "next/node_modules/next": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.7.tgz", - "integrity": "sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", "dependencies": { - "@next/env": "13.4.7", - "@swc/helpers": "0.5.1", + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0", - "zod": "3.21.4" + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.7", - "@next/swc-darwin-x64": "13.4.7", - "@next/swc-linux-arm64-gnu": "13.4.7", - "@next/swc-linux-arm64-musl": "13.4.7", - "@next/swc-linux-x64-gnu": "13.4.7", - "@next/swc-linux-x64-musl": "13.4.7", - "@next/swc-win32-arm64-msvc": "13.4.7", - "@next/swc-win32-ia32-msvc": "13.4.7", - "@next/swc-win32-x64-msvc": "13.4.7" + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "fibers": ">= 3.1.0", + "@playwright/test": "^1.41.2", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" @@ -11539,7 +9824,7 @@ "@opentelemetry/api": { "optional": true }, - "fibers": { + "@playwright/test": { "optional": true }, "sass": { @@ -11547,20 +9832,10 @@ } } }, - "next/node_modules/optimism": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.17.5.tgz", - "integrity": "sha512-TEcp8ZwK1RczmvMnvktxHSF2tKgMWjJ71xEFGX5ApLh67VsMSTy1ZUlipJw8W+KaqgOmQ+4pqwkeivY89j+4Vw==", - "dependencies": { - "@wry/context": "^0.7.0", - "@wry/trie": "^0.4.3", - "tslib": "^2.3.0" - } - }, "next/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -11569,10 +9844,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -11592,6 +9871,14 @@ "node": ">=14.17" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -11632,18 +9919,19 @@ } }, "node_modules/@apollo/client": { - "version": "3.7.16", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.7.16.tgz", - "integrity": "sha512-rdhoc7baSD7ZzcjavEpYN8gZJle1KhjEKj4SJeMgBpcnO4as7oXUVU4LtFpotzZdFlo57qaLrNzfvppSTsKvZQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.10.1.tgz", + "integrity": "sha512-QNacQBZzJla5UQ/LLBXJWM7/1v1C5cfpMQPAFjW4hg4T54wHWbg4Dr+Dp6N+hy/ygu8tepdM+/y/5VFLZhovlQ==", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", - "@wry/context": "^0.7.0", - "@wry/equality": "^0.5.0", - "@wry/trie": "^0.4.0", + "@wry/caches": "^1.0.0", + "@wry/equality": "^0.5.6", + "@wry/trie": "^0.5.0", "graphql-tag": "^2.12.6", "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.16.2", + "optimism": "^0.18.0", "prop-types": "^15.7.2", + "rehackt": "^0.1.0", "response-iterator": "^0.2.6", "symbol-observable": "^4.0.0", "ts-invariant": "^0.10.3", @@ -11651,7 +9939,7 @@ "zen-observable-ts": "^1.2.5" }, "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql": "^15.0.0 || ^16.0.0", "graphql-ws": "^5.5.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -11673,109 +9961,45 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", - "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -11785,6 +10009,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/core/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -11848,13 +10077,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -11884,21 +10113,18 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", - "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -12051,32 +10277,32 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -12165,9 +10391,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "engines": { "node": ">=6.9.0" } @@ -12181,9 +10407,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "engines": { "node": ">=6.9.0" } @@ -12203,26 +10429,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -12293,9 +10520,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -13795,32 +12022,32 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -13849,11 +12076,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -13867,9 +12094,9 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "node_modules/@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", + "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==" }, "node_modules/@csstools/postcss-cascade-layers": { "version": "1.1.1", @@ -14137,9 +12364,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "cpu": [ "arm" ], @@ -14153,9 +12380,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "cpu": [ "arm64" ], @@ -14169,9 +12396,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "cpu": [ "x64" ], @@ -14185,9 +12412,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], @@ -14201,9 +12428,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], @@ -14217,9 +12444,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "cpu": [ "arm64" ], @@ -14233,9 +12460,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "cpu": [ "x64" ], @@ -14249,9 +12476,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "cpu": [ "arm" ], @@ -14265,9 +12492,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "cpu": [ "arm64" ], @@ -14281,9 +12508,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "cpu": [ "ia32" ], @@ -14297,9 +12524,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "cpu": [ "loong64" ], @@ -14313,9 +12540,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "cpu": [ "mips64el" ], @@ -14329,9 +12556,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "cpu": [ "ppc64" ], @@ -14345,9 +12572,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "cpu": [ "riscv64" ], @@ -14361,9 +12588,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "cpu": [ "s390x" ], @@ -14377,9 +12604,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "cpu": [ "x64" ], @@ -14393,9 +12620,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "cpu": [ "x64" ], @@ -14409,9 +12636,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "cpu": [ "x64" ], @@ -14425,9 +12652,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "cpu": [ "x64" ], @@ -14441,9 +12668,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "cpu": [ "arm64" ], @@ -14457,9 +12684,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "cpu": [ "ia32" ], @@ -14473,9 +12700,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "cpu": [ "x64" ], @@ -14503,21 +12730,21 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -14569,9 +12796,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -14626,13 +12853,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" + }, "node_modules/@graphql-tools/merge": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.0.tgz", @@ -14690,18 +12922,23 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" + }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -15248,13 +13485,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -15269,9 +13506,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -15291,33 +13528,28 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, "node_modules/@next/env": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz", - "integrity": "sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw==" + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz", - "integrity": "sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", "cpu": [ "arm64" ], @@ -15330,9 +13562,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz", - "integrity": "sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", "cpu": [ "x64" ], @@ -15345,9 +13577,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz", - "integrity": "sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", "cpu": [ "arm64" ], @@ -15360,9 +13592,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz", - "integrity": "sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", "cpu": [ "arm64" ], @@ -15375,9 +13607,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz", - "integrity": "sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", "cpu": [ "x64" ], @@ -15390,9 +13622,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz", - "integrity": "sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", "cpu": [ "x64" ], @@ -15405,9 +13637,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz", - "integrity": "sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", "cpu": [ "arm64" ], @@ -15420,9 +13652,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz", - "integrity": "sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", "cpu": [ "ia32" ], @@ -15435,9 +13667,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz", - "integrity": "sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", "cpu": [ "x64" ], @@ -15457,26 +13689,6 @@ "eslint-scope": "5.1.1" } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -15509,29 +13721,57 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@playwright/test": { - "version": "1.35.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz", - "integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==", - "dev": true, + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", + "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", "dependencies": { - "@types/node": "*", - "playwright-core": "1.35.1" + "playwright": "1.43.1" }, "bin": { "playwright": "cli.js" }, "engines": { "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "2.3.2" } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", + "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", "dependencies": { "ansi-html-community": "^0.0.8", "common-path-prefix": "^3.0.0", @@ -15550,7 +13790,7 @@ "@types/webpack": "4.x || 5.x", "react-refresh": ">=0.10.0 <1.0.0", "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", + "type-fest": ">=0.17.0 <5.0.0", "webpack": ">=4.43.0 <6.0.0", "webpack-dev-server": "3.x || 4.x", "webpack-hot-middleware": "2.x", @@ -16100,10 +14340,18 @@ "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, "node_modules/@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -16196,9 +14444,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -16214,9 +14462,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/express": { "version": "4.17.17", @@ -16313,6 +14561,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -16438,9 +14694,9 @@ } }, "node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dependencies": { "@types/yargs-parser": "*" } @@ -16451,16 +14707,16 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", - "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/type-utils": "5.60.0", - "@typescript-eslint/utils": "5.60.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -16505,11 +14761,11 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.60.0.tgz", - "integrity": "sha512-ovid3u7CNBrr0Ct35LUPkNYH4e+z4Kc6dPfSG99oMmH9SfoEoefq09uSnJI4mUb/UM7a/peVM03G+MzLxrD16g==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", "dependencies": { - "@typescript-eslint/utils": "5.60.0" + "@typescript-eslint/utils": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -16523,13 +14779,13 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", - "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dependencies": { - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -16570,12 +14826,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -16586,12 +14842,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", - "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.0", - "@typescript-eslint/utils": "5.60.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -16633,9 +14889,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -16645,12 +14901,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -16692,16 +14948,16 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -16716,32 +14972,12 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dependencies": { - "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -16752,10 +14988,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -16772,9 +15013,9 @@ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", @@ -16792,14 +15033,14 @@ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -16824,26 +15065,26 @@ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -16851,22 +15092,22 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -16875,18 +15116,29 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, + "node_modules/@wry/caches": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", + "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wry/context": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz", - "integrity": "sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", + "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -16906,9 +15158,9 @@ } }, "node_modules/@wry/trie": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", - "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", + "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", "dependencies": { "tslib": "^2.3.0" }, @@ -16950,9 +15202,9 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -17051,11 +15303,23 @@ } } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -17148,6 +15412,14 @@ "node": ">=8" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -17377,15 +15649,23 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "node_modules/asynckit": { "version": "0.4.0", @@ -17401,9 +15681,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "funding": [ { "type": "opencollective", @@ -17412,12 +15692,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -17765,11 +16049,14 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bluebird": { @@ -17777,13 +16064,18 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -17791,7 +16083,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -17887,9 +16179,9 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "funding": [ { "type": "opencollective", @@ -17905,10 +16197,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -17960,6 +16252,61 @@ "node": ">= 0.8" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -17972,6 +16319,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -18021,9 +16398,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001508", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", - "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", + "version": "1.0.30001612", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", + "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", "funding": [ { "type": "opencollective", @@ -18119,15 +16496,9 @@ "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -18140,6 +16511,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -18155,6 +16529,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -18164,9 +16546,9 @@ } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "funding": [ { "type": "github", @@ -18183,9 +16565,9 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, "node_modules/clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dependencies": { "source-map": "~0.6.0" }, @@ -18201,6 +16583,14 @@ "node": ">=0.10.0" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, "node_modules/cli-boxes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", @@ -18535,9 +16925,9 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -18562,9 +16952,9 @@ } }, "node_modules/core-js": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.0.tgz", - "integrity": "sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==", + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.0.tgz", + "integrity": "sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -18660,9 +17050,9 @@ } }, "node_modules/css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", "engines": { "node": "^10 || ^12 || >=14" }, @@ -18688,18 +17078,18 @@ } }, "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">= 12.13.0" @@ -18709,7 +17099,16 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/css-minimizer-webpack-plugin": { @@ -18841,9 +17240,9 @@ } }, "node_modules/cssdb": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.6.0.tgz", - "integrity": "sha512-Nna7rph8V0jC6+JBY4Vk4ndErUmfJfV6NJCaZdurL0omggabiy+QB2HCQtu5c/ACLZ0I7REv7A4QyPIoYzZx0w==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", "funding": [ { "type": "opencollective", @@ -19001,6 +17400,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -19093,6 +17501,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -19190,9 +17610,9 @@ "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" }, "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -19344,9 +17764,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, @@ -19358,9 +17778,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.440", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", - "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==" + "version": "1.4.749", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.749.tgz", + "integrity": "sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q==" }, "node_modules/emittery": { "version": "0.8.1", @@ -19394,10 +17814,18 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -19414,6 +17842,17 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -19524,10 +17963,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, "hasInstallScript": true, "bin": { @@ -19537,28 +18010,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "node_modules/escalade": { @@ -19663,26 +18136,27 @@ } }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -19692,7 +18166,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -19702,9 +18175,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -19993,11 +18465,11 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -20017,9 +18489,9 @@ } }, "node_modules/eslint-plugin-testing-library": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", - "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", "dependencies": { "@typescript-eslint/utils": "^5.58.0" }, @@ -20032,24 +18504,54 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "estraverse": "^4.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -20198,10 +18700,25 @@ } } }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -20244,17 +18761,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -20262,16 +18768,35 @@ "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" } }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -20345,6 +18870,15 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -20403,16 +18937,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -20491,6 +19025,31 @@ } ] }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -20710,9 +19269,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -20907,15 +19466,15 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { @@ -20939,6 +19498,17 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs-monkey": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", @@ -20963,9 +19533,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -21061,15 +19634,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -21188,11 +19769,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -21318,6 +19894,75 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -21372,14 +20017,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -21392,9 +20029,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "funding": [ { "type": "github", @@ -21432,9 +20069,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", @@ -21450,7 +20087,16 @@ "url": "https://opencollective.com/html-webpack-plugin" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/htmlparser2": { @@ -21646,9 +20292,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { "node": ">= 4" } @@ -21711,6 +20357,19 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -21744,13 +20403,32 @@ } }, "node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "engines": { "node": ">= 10" } }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -21806,6 +20484,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -21818,16 +20501,27 @@ } }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -21842,6 +20536,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -21856,6 +20562,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -21956,6 +20673,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-port-reachable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", @@ -22089,9 +20817,9 @@ } }, "node_modules/is-what": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.15.tgz", - "integrity": "sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA==", + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", "engines": { "node": ">=12.13" }, @@ -22120,6 +20848,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -23294,9 +22030,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dependencies": { "@types/yargs-parser": "*" } @@ -23467,9 +22203,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.0.tgz", + "integrity": "sha512-wRiUsea88TjKDc4FBEn+sLvIDesp6brMbGWnJGjew2waAc9evdhja/2LvePc898HJbHw0L+MTWy7NhpnELAvLQ==" }, "node_modules/jest-watch-typeahead/node_modules/slash": { "version": "4.0.0", @@ -23954,6 +22690,34 @@ "node": ">= 4.0.0" } }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/memory-fs/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -24031,11 +22795,12 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dependencies": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { "node": ">= 12.13.0" @@ -24090,6 +22855,72 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -24129,9 +22960,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -24172,6 +23003,11 @@ "resolved": "next", "link": true }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -24195,9 +23031,9 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -24440,18 +23276,20 @@ } }, "node_modules/optimism": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.16.2.tgz", - "integrity": "sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz", + "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==", "dependencies": { + "@wry/caches": "^1.0.0", "@wry/context": "^0.7.0", - "@wry/trie": "^0.3.0" + "@wry/trie": "^0.4.3", + "tslib": "^2.3.0" } }, "node_modules/optimism/node_modules/@wry/trie": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.3.2.tgz", - "integrity": "sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", + "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", "dependencies": { "tslib": "^2.3.0" }, @@ -24460,16 +23298,16 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -24503,6 +23341,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -24794,26 +23646,26 @@ } }, "node_modules/playwright": { - "version": "1.35.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.35.1.tgz", - "integrity": "sha512-NbwBeGJLu5m7VGM0+xtlmLAH9VUfWwYOhUi/lSEDyGg46r1CA9RWlvoc5yywxR9AzQb0mOCm7bWtOXV7/w43ZA==", - "dev": true, - "hasInstallScript": true, + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", "dependencies": { - "playwright-core": "1.35.1" + "playwright-core": "1.43.1" }, "bin": { "playwright": "cli.js" }, "engines": { "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.35.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz", - "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==", - "dev": true, + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", "bin": { "playwright-core": "cli.js" }, @@ -24822,9 +23674,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -24840,9 +23692,9 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -25278,20 +24130,26 @@ } }, "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { "node": ">= 14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" @@ -25305,10 +24163,24 @@ } } }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -25449,9 +24321,9 @@ } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -25460,9 +24332,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -25476,9 +24348,9 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -25902,9 +24774,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -26078,6 +24950,11 @@ "asap": "~2.0.6" } }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -26120,11 +24997,25 @@ "node": ">= 0.10" } }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -26203,9 +25094,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -26251,9 +25142,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.0.tgz", + "integrity": "sha512-RPutkJftSAldDibyrjuku7q11d3oy6wKOyPe5K1HA/HwwrXcEqBdHsLypkC2FFYjP7bPUa6gbzSBhw4sY2JcDg==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -26368,15 +25259,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-zaKdLBftQJnvb7FtDIpZtsAIb2MZU087RM8bRDZU8LVCCFYjPTsDZJNFUWPcVz3HFSN1n/caxi0ca4B/aaVQGQ==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.1" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.0" } }, "node_modules/react-error-overlay": { @@ -26630,6 +25521,23 @@ "jsesc": "bin/jsesc" } }, + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -26691,11 +25599,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -27023,9 +25931,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.1.tgz", + "integrity": "sha512-5GKS5JGfiah1O38Vfa9srZE4s3wdHbwjlCrvIookrg2FO9aIwKLOJXuJQFlEfNcVSOXuaL2hzDeY20uVXcUtrw==", "dependencies": { "loose-envify": "^1.1.0" } @@ -27081,10 +25989,11 @@ "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -27092,9 +26001,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -27158,9 +26067,9 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dependencies": { "randombytes": "^2.1.0" } @@ -27389,9 +26298,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -27514,6 +26423,17 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -27561,32 +26481,13 @@ } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -27761,9 +26662,9 @@ } }, "node_modules/style-loader": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", - "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "engines": { "node": ">= 12.13.0" }, @@ -27841,34 +26742,15 @@ "node": ">= 6" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/superjson": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-1.12.4.tgz", - "integrity": "sha512-vkpPQAxdCg9SLfPv5GPC5fnGrui/WryktoN9O5+Zif/14QIMjw+RITf/5LbBh+9QpBFb3KNvJth+puz2H8o6GQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", "dependencies": { "copy-anything": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=16" } }, "node_modules/supports-color": { @@ -28112,6 +26994,46 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -28164,9 +27086,9 @@ } }, "node_modules/terser": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", - "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", + "version": "5.30.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.4.tgz", + "integrity": "sha512-xRdd0v64a8mFK9bnsKVdoNP9GQIKUAaJPTaqEQDL4w/J8WaW4sWXXoMZ+6SimPkfT5bElreXf8m9HnmPc3E1BQ==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -28181,15 +27103,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -28312,9 +27234,9 @@ } }, "node_modules/tough-cookie/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -28339,9 +27261,9 @@ } }, "node_modules/tr46/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -28398,9 +27320,9 @@ } }, "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -28421,6 +27343,11 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -28444,7 +27371,7 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=12.20" }, @@ -28547,6 +27474,22 @@ "node": ">=4" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -28559,9 +27502,9 @@ } }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "engines": { "node": ">= 10.0.0" } @@ -28589,9 +27532,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -28636,9 +27579,9 @@ } }, "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -28652,6 +27595,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dependencies": { + "inherits": "2.0.1" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -28671,6 +27622,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" + }, "node_modules/utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", @@ -28758,9 +27714,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -28786,33 +27742,33 @@ } }, "node_modules/webpack": { - "version": "5.88.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", - "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.16.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -28832,9 +27788,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -28880,9 +27836,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -28912,7 +27868,7 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", + "webpack-dev-middleware": "^5.3.4", "ws": "^8.13.0" }, "bin": { @@ -28956,9 +27912,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { "node": ">=10.0.0" }, @@ -29018,26 +27974,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -29251,9 +28187,9 @@ } }, "node_modules/workbox-build/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -29609,22 +28545,14 @@ "zen-observable": "0.8.15" } }, - "node_modules/zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node-esm": { "name": "dual-module-test-esm", "version": "1.0.0", "license": "MIT", "dependencies": { - "@apollo/client": "^3.7.16", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "resolve-esm": "^1.4.0" @@ -29635,9 +28563,9 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@apollo/client": "^3.7.16", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "resolve-esm": "^1.4.0" @@ -29652,10 +28580,10 @@ "vite": { "version": "0.0.0", "dependencies": { - "@apollo/client": "^3.7.16", + "@apollo/client": "^3.10.1", "graphql": "^16.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "@playwright/test": "*", @@ -29676,8 +28604,9 @@ "vite-swc": { "version": "0.0.0", "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { "@playwright/test": "*", @@ -29708,9 +28637,9 @@ } }, "vite-swc/node_modules/rollup": { - "version": "3.25.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.3.tgz", - "integrity": "sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -29724,9 +28653,9 @@ } }, "vite-swc/node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -29737,14 +28666,14 @@ } }, "vite-swc/node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -29752,12 +28681,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -29770,6 +28703,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -29803,18 +28739,18 @@ } }, "vite/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.1.tgz", + "integrity": "sha512-iZiRCtNGY3QYP3pYOSSBOvQmBpQTcJccr/VcK2blpJrpPTUDjeN51mxm5nsrkCzBwsbGUj+TN9q2oPz5E13FLg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "vite/node_modules/rollup": { - "version": "3.25.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.3.tgz", - "integrity": "sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -29828,9 +28764,9 @@ } }, "vite/node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -29841,14 +28777,14 @@ } }, "vite/node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -29856,12 +28792,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -29874,6 +28814,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, diff --git a/integration-tests/package.json b/integration-tests/package.json index de1ada8ea80..c621c2ef6df 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -2,8 +2,8 @@ "name": "integration-tests", "license": "UNLICENSED", "overrides": { - "@playwright/test": "^1.35.1", - "playwright": "^1.35.1", + "@playwright/test": "1.43.1", + "playwright": "1.43.1", "serve": "^14.2.0" }, "workspaces": [ diff --git a/integration-tests/peerdeps-tsc/.gitignore b/integration-tests/peerdeps-tsc/.gitignore new file mode 100644 index 00000000000..db6846cb27a --- /dev/null +++ b/integration-tests/peerdeps-tsc/.gitignore @@ -0,0 +1,5 @@ +# explicitly avoiding to check in this one +# so we run this test always with the latest version +package-lock.json +dist +node_modules diff --git a/integration-tests/peerdeps-tsc/package.json b/integration-tests/peerdeps-tsc/package.json new file mode 100644 index 00000000000..15fa22cb969 --- /dev/null +++ b/integration-tests/peerdeps-tsc/package.json @@ -0,0 +1,20 @@ +{ + "name": "peerdeps-tsc", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "graphql": "^16.0.0", + "graphql-ws": "^5.5.5", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "subscriptions-transport-ws": "^0.11.0", + "typescript": "latest" + } +} diff --git a/integration-tests/peerdeps-tsc/src/index.ts b/integration-tests/peerdeps-tsc/src/index.ts new file mode 100644 index 00000000000..a0266127931 --- /dev/null +++ b/integration-tests/peerdeps-tsc/src/index.ts @@ -0,0 +1 @@ +export * from "@apollo/client"; diff --git a/integration-tests/peerdeps-tsc/tsconfig.json b/integration-tests/peerdeps-tsc/tsconfig.json new file mode 100644 index 00000000000..1dff851d2ac --- /dev/null +++ b/integration-tests/peerdeps-tsc/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "commonjs", + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": false, + "types": ["react", "react-dom"], + "lib": ["es2018", "dom"] + } +} diff --git a/integration-tests/vite-swc/package.json b/integration-tests/vite-swc/package.json index 8804f0e663c..c2bf765ad74 100644 --- a/integration-tests/vite-swc/package.json +++ b/integration-tests/vite-swc/package.json @@ -12,10 +12,12 @@ "test": "playwright test" }, "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@apollo/client": "^3.10.1", + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { + "@playwright/test": "*", "@types/react": "^18.0.37", "@types/react-dom": "^18.0.11", "@typescript-eslint/eslint-plugin": "^5.59.0", @@ -24,10 +26,18 @@ "eslint": "^8.38.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.3.4", - "typescript": "^5.0.2", - "vite": "^4.3.9", - "shared": "*", "playwright": "*", - "@playwright/test": "*" + "shared": "*", + "typescript": "^5.0.2", + "vite": "^4.3.9" + }, + "overrides": { + "next": { + "@playwright/test": "$@playwright/test" + }, + "@apollo/client": { + "react": "$react", + "react-dom": "$react-dom" + } } } diff --git a/integration-tests/vite-swc/src/App.tsx b/integration-tests/vite-swc/src/App.tsx index cf87849afbd..189ee26af73 100644 --- a/integration-tests/vite-swc/src/App.tsx +++ b/integration-tests/vite-swc/src/App.tsx @@ -55,9 +55,9 @@ export default function App() { function Main() { const { data } = useQuery(QUERY); - return data ? ( -
    {data?.products.map(({ id, title }) =>
  • {title}
  • )}
- ) : ( - <>loading - ); + return data ? +
    + {data?.products.map(({ id, title }) =>
  • {title}
  • )} +
+ : <>loading; } diff --git a/integration-tests/vite/package.json b/integration-tests/vite/package.json index a19b89b6d6a..7dd7cfba80d 100644 --- a/integration-tests/vite/package.json +++ b/integration-tests/vite/package.json @@ -12,12 +12,13 @@ "test": "playwright test" }, "dependencies": { - "@apollo/client": "^3.7.16", + "@apollo/client": "^3.10.1", "graphql": "^16.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.0", + "react-dom": "^18.3.0" }, "devDependencies": { + "@playwright/test": "*", "@types/react": "^18.0.37", "@types/react-dom": "^18.0.11", "@typescript-eslint/eslint-plugin": "^5.59.0", @@ -26,10 +27,9 @@ "eslint": "^8.38.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.3.4", - "typescript": "^5.0.2", - "vite": "^4.3.9", - "shared": "*", "playwright": "*", - "@playwright/test": "*" + "shared": "*", + "typescript": "^5.0.2", + "vite": "^4.3.9" } } diff --git a/integration-tests/vite/src/App.tsx b/integration-tests/vite/src/App.tsx index cf87849afbd..189ee26af73 100644 --- a/integration-tests/vite/src/App.tsx +++ b/integration-tests/vite/src/App.tsx @@ -55,9 +55,9 @@ export default function App() { function Main() { const { data } = useQuery(QUERY); - return data ? ( -
    {data?.products.map(({ id, title }) =>
  • {title}
  • )}
- ) : ( - <>loading - ); + return data ? +
    + {data?.products.map(({ id, title }) =>
  • {title}
  • )} +
+ : <>loading; } diff --git a/netlify.toml b/netlify.toml index 6e8d1ace9c9..363e4f0818d 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,14 +1,13 @@ [build] publish = "docs/public" - command = "npm run typedoc" - -[build.environment] - NODE_VERSION = "18" + command = "npm run typedoc; npm run docmodel > docs/public/log.txt || true" [context.deploy-preview.build] base = "docs" - ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ." + ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../src" command = """\ + npm i + npm run docmodel cd ../ rm -rf monodocs git clone https://github.com/apollographql/docs --branch main --single-branch monodocs diff --git a/package-lock.json b/package-lock.json index 41db6d1fd79..0684be839e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,24 @@ { "name": "@apollo/client", - "version": "3.8.6", + "version": "3.10.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@apollo/client", - "version": "3.8.6", + "version": "3.10.4", "hasInstallScript": true, "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", - "@wry/context": "^0.7.3", + "@wry/caches": "^1.0.0", "@wry/equality": "^0.5.6", - "@wry/trie": "^0.4.3", + "@wry/trie": "^0.5.0", "graphql-tag": "^2.12.6", "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.17.5", + "optimism": "^0.18.0", "prop-types": "^15.7.2", + "rehackt": "^0.1.0", "response-iterator": "^0.2.6", "symbol-observable": "^4.0.0", "ts-invariant": "^0.10.3", @@ -25,85 +26,94 @@ "zen-observable-ts": "^1.2.5" }, "devDependencies": { - "@arethetypeswrong/cli": "0.12.2", - "@babel/parser": "7.23.0", - "@changesets/changelog-github": "0.4.8", - "@changesets/cli": "2.26.2", - "@graphql-tools/schema": "10.0.0", - "@microsoft/api-extractor": "7.38.0", + "@arethetypeswrong/cli": "0.15.3", + "@babel/parser": "7.24.5", + "@changesets/changelog-github": "0.5.0", + "@changesets/cli": "2.27.1", + "@graphql-tools/merge": "9.0.3", + "@graphql-tools/schema": "10.0.3", + "@graphql-tools/utils": "10.2.0", + "@microsoft/api-extractor": "7.43.2", "@rollup/plugin-node-resolve": "11.2.1", - "@size-limit/esbuild-why": "8.2.6", - "@size-limit/preset-small-lib": "8.2.6", - "@testing-library/jest-dom": "5.17.0", - "@testing-library/react": "14.0.0", + "@size-limit/esbuild-why": "11.1.2", + "@size-limit/preset-small-lib": "11.1.2", + "@testing-library/jest-dom": "6.4.5", + "@testing-library/react": "15.0.6", "@testing-library/react-12": "npm:@testing-library/react@^12", - "@testing-library/user-event": "14.5.1", - "@tsconfig/node20": "20.1.2", - "@types/bytes": "3.1.3", - "@types/fetch-mock": "7.3.7", + "@testing-library/user-event": "14.5.2", + "@tsconfig/node20": "20.1.4", + "@types/bytes": "3.1.4", + "@types/fetch-mock": "7.3.8", "@types/glob": "8.1.0", - "@types/hoist-non-react-statics": "3.3.4", - "@types/jest": "29.5.6", - "@types/lodash": "4.14.200", - "@types/node": "20.8.9", - "@types/node-fetch": "2.6.7", - "@types/react": "18.2.33", - "@types/react-dom": "18.2.14", - "@types/use-sync-external-store": "0.0.5", - "@typescript-eslint/eslint-plugin": "6.7.5", - "@typescript-eslint/parser": "6.7.5", - "@typescript-eslint/rule-tester": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "acorn": "8.10.0", + "@types/hoist-non-react-statics": "3.3.5", + "@types/jest": "29.5.12", + "@types/lodash": "4.17.1", + "@types/node": "20.12.10", + "@types/node-fetch": "2.6.11", + "@types/react": "18.3.1", + "@types/react-dom": "18.3.0", + "@types/relay-runtime": "14.1.23", + "@types/use-sync-external-store": "0.0.6", + "@typescript-eslint/eslint-plugin": "7.9.0", + "@typescript-eslint/parser": "7.9.0", + "@typescript-eslint/rule-tester": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "acorn": "8.11.3", "blob-polyfill": "7.0.20220408", "bytes": "3.1.2", - "cross-fetch": "3.1.8", - "eslint": "8.51.0", + "cross-fetch": "4.0.0", + "eslint": "8.57.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "npm:@phryneas/eslint-plugin-import@2.27.5-pr.2813.2817.199971c", - "eslint-plugin-local-rules": "2.0.0", - "eslint-plugin-testing-library": "5.11.1", - "expect-type": "0.17.3", + "eslint-plugin-local-rules": "2.0.1", + "eslint-plugin-react-compiler": "0.0.0-experimental-c8b3f72-20240517", + "eslint-plugin-react-hooks": "4.6.2", + "eslint-plugin-testing-library": "6.2.2", + "expect-type": "0.19.0", "fetch-mock": "9.11.0", "glob": "8.1.0", "graphql": "16.8.1", - "graphql-ws": "5.14.2", + "graphql-ws": "5.16.0", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jest-junit": "16.0.0", "lodash": "4.17.21", - "patch-package": "7.0.2", - "prettier": "3.0.3", - "react": "18.2.0", + "patch-package": "8.0.0", + "prettier": "3.1.1", + "react": "18.3.1", "react-17": "npm:react@^17", - "react-dom": "18.2.0", + "react-19": "npm:react@19.0.0-rc-cc1ec60d0d-20240607", + "react-dom": "18.3.1", "react-dom-17": "npm:react-dom@^17", - "react-error-boundary": "3.1.4", - "recast": "0.23.4", + "react-dom-19": "npm:react-dom@19.0.0-rc-cc1ec60d0d-20240607", + "react-error-boundary": "4.0.13", + "recast": "0.23.6", "resolve": "1.22.8", "rimraf": "5.0.5", "rollup": "2.79.1", + "rollup-plugin-cleanup": "3.2.1", "rollup-plugin-terser": "7.0.2", "rxjs": "7.8.1", - "size-limit": "8.2.6", + "size-limit": "11.1.2", "subscriptions-transport-ws": "0.11.0", - "terser": "5.21.0", - "ts-api-utils": "1.0.3", - "ts-jest": "29.1.1", + "terser": "5.31.0", + "ts-api-utils": "1.3.0", + "ts-jest": "29.1.2", "ts-jest-resolver": "2.0.1", - "ts-node": "10.9.1", + "ts-morph": "22.0.0", + "ts-node": "10.9.2", "typedoc": "0.25.0", - "typescript": "5.2.2", + "typescript": "5.4.5", "wait-for-observables": "1.0.3", - "web-streams-polyfill": "3.2.1", - "whatwg-fetch": "3.6.19" + "web-streams-polyfill": "4.0.0", + "whatwg-fetch": "3.6.20" }, "engines": { - "npm": "^7.20.3 || ^8.0.0 || ^9.0.0" + "npm": "^7.20.3 || ^8.0.0 || ^9.0.0 || ^10.0.0" }, "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql": "^15.0.0 || ^16.0.0", "graphql-ws": "^5.5.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -134,11 +144,24 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@andrewbranch/untar.js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", @@ -146,22 +169,24 @@ "dev": true }, "node_modules/@arethetypeswrong/cli": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.12.2.tgz", - "integrity": "sha512-SE4Rqy8LM8zgRLeVXZqFIOg4w4TCDG2AMguuZDDRcrUmVQj7phW0tWJnKwsZtyJ6SdqXTIzWvGYiUJiHg2hb9w==", + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.15.3.tgz", + "integrity": "sha512-sIMA9ZJBWDEg1+xt5RkAEflZuf8+PO8SdKj17x6PtETuUho+qlZJg4DgmKc3q+QwQ9zOB5VLK6jVRbFdNLdUIA==", "dev": true, "dependencies": { - "@arethetypeswrong/core": "0.12.2", + "@arethetypeswrong/core": "0.15.1", "chalk": "^4.1.2", "cli-table3": "^0.6.3", "commander": "^10.0.1", - "marked": "^5.1.0", - "marked-terminal": "^5.2.0", - "node-fetch": "^2.6.4", + "marked": "^9.1.2", + "marked-terminal": "^6.0.0", "semver": "^7.5.4" }, "bin": { "attw": "dist/index.js" + }, + "engines": { + "node": ">=18" } }, "node_modules/@arethetypeswrong/cli/node_modules/commander": { @@ -174,9 +199,9 @@ } }, "node_modules/@arethetypeswrong/cli/node_modules/marked": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz", - "integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -186,9 +211,9 @@ } }, "node_modules/@arethetypeswrong/cli/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -201,17 +226,20 @@ } }, "node_modules/@arethetypeswrong/core": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.12.2.tgz", - "integrity": "sha512-ez/quGfC6RVg7VrwCgMVreJ01jbkfJQRNxOG7Bpl4YGcPRs45ZE1AzpHiIdzqfwFg9EBVSaewaffrsK5MVbALw==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.15.1.tgz", + "integrity": "sha512-FYp6GBAgsNz81BkfItRz8RLZO03w5+BaeiPma1uCfmxTnxbtuMrI/dbzGiOk8VghO108uFI0oJo0OkewdSHw7g==", "dev": true, "dependencies": { "@andrewbranch/untar.js": "^1.0.3", - "fetch-ponyfill": "^7.1.0", - "fflate": "^0.7.4", + "fflate": "^0.8.2", "semver": "^7.5.4", - "typescript": "^5.2.2", + "ts-expose-internals-conditionally": "1.0.0-empty.0", + "typescript": "5.3.3", "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/@arethetypeswrong/core/node_modules/semver": { @@ -229,48 +257,62 @@ "node": ">=10" } }, + "node_modules/@arethetypeswrong/core/node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.24.6", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.16.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", + "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", - "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7", - "convert-source-map": "^1.7.0", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", + "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helpers": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/traverse": "^7.24.6", + "@babel/types": "^7.24.6", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -280,48 +322,107 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/@babel/core/node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/generator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz", - "integrity": "sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.24.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", + "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" + "@babel/compat-data": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz", + "integrity": "sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -331,81 +432,89 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", "dev": true, "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz", + "integrity": "sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", + "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", + "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz", + "integrity": "sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -420,80 +529,109 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz", + "integrity": "sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz", + "integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz", + "integrity": "sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", + "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", + "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", - "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -516,7 +654,7 @@ "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -535,9 +673,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -546,6 +684,23 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -736,48 +891,72 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/traverse": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz", - "integrity": "sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7", - "debug": "^4.1.0", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -791,16 +970,16 @@ "dev": true }, "node_modules/@changesets/apply-release-plan": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz", - "integrity": "sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.0.tgz", + "integrity": "sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/config": "^2.3.1", - "@changesets/get-version-range-type": "^0.3.2", - "@changesets/git": "^2.0.0", - "@changesets/types": "^5.2.1", + "@changesets/config": "^3.0.0", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", @@ -842,23 +1021,23 @@ } }, "node_modules/@changesets/assemble-release-plan": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.2.4.tgz", - "integrity": "sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.0.tgz", + "integrity": "sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.0.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "node_modules/@changesets/assemble-release-plan/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -871,55 +1050,54 @@ } }, "node_modules/@changesets/changelog-git": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.14.tgz", - "integrity": "sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.0.tgz", + "integrity": "sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==", "dev": true, "dependencies": { - "@changesets/types": "^5.2.1" + "@changesets/types": "^6.0.0" } }, "node_modules/@changesets/changelog-github": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.4.8.tgz", - "integrity": "sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.5.0.tgz", + "integrity": "sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==", "dev": true, "dependencies": { - "@changesets/get-github-info": "^0.5.2", - "@changesets/types": "^5.2.1", + "@changesets/get-github-info": "^0.6.0", + "@changesets/types": "^6.0.0", "dotenv": "^8.1.0" } }, "node_modules/@changesets/cli": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.26.2.tgz", - "integrity": "sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig==", + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.27.1.tgz", + "integrity": "sha512-iJ91xlvRnnrJnELTp4eJJEOPjgpF3NOh4qeQehM6Ugiz9gJPRZ2t+TsXun6E3AMN4hScZKjqVXl0TX+C7AB3ZQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/apply-release-plan": "^6.1.4", - "@changesets/assemble-release-plan": "^5.2.4", - "@changesets/changelog-git": "^0.1.14", - "@changesets/config": "^2.3.1", - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/get-release-plan": "^3.0.17", - "@changesets/git": "^2.0.0", - "@changesets/logger": "^0.0.5", - "@changesets/pre": "^1.0.14", - "@changesets/read": "^0.5.9", - "@changesets/types": "^5.2.1", - "@changesets/write": "^0.2.3", + "@changesets/apply-release-plan": "^7.0.0", + "@changesets/assemble-release-plan": "^6.0.0", + "@changesets/changelog-git": "^0.2.0", + "@changesets/config": "^3.0.0", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.0.0", + "@changesets/get-release-plan": "^4.0.0", + "@changesets/git": "^3.0.0", + "@changesets/logger": "^0.1.0", + "@changesets/pre": "^2.0.0", + "@changesets/read": "^0.6.0", + "@changesets/types": "^6.0.0", + "@changesets/write": "^0.3.0", "@manypkg/get-packages": "^1.1.3", - "@types/is-ci": "^3.0.0", "@types/semver": "^7.5.0", "ansi-colors": "^4.1.3", "chalk": "^2.1.0", + "ci-info": "^3.7.0", "enquirer": "^2.3.0", "external-editor": "^3.1.0", "fs-extra": "^7.0.1", "human-id": "^1.0.2", - "is-ci": "^3.0.1", "meow": "^6.0.0", "outdent": "^0.5.0", "p-limit": "^2.2.0", @@ -958,9 +1136,9 @@ } }, "node_modules/@changesets/cli/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -985,36 +1163,36 @@ } }, "node_modules/@changesets/config": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-2.3.1.tgz", - "integrity": "sha512-PQXaJl82CfIXddUOppj4zWu+987GCw2M+eQcOepxN5s+kvnsZOwjEJO3DH9eVy+OP6Pg/KFEWdsECFEYTtbg6w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.0.0.tgz", + "integrity": "sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==", "dev": true, "dependencies": { - "@changesets/errors": "^0.1.4", - "@changesets/get-dependents-graph": "^1.3.6", - "@changesets/logger": "^0.0.5", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.0.0", + "@changesets/logger": "^0.1.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.2" } }, "node_modules/@changesets/errors": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.1.4.tgz", - "integrity": "sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", "dev": true, "dependencies": { "extendable-error": "^0.1.5" } }, "node_modules/@changesets/get-dependents-graph": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.6.tgz", - "integrity": "sha512-Q/sLgBANmkvUm09GgRsAvEtY3p1/5OCzgBE5vX3vgb5CvW0j7CEljocx5oPXeQSNph6FXulJlXV3Re/v3K3P3Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.0.0.tgz", + "integrity": "sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==", "dev": true, "dependencies": { - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "chalk": "^2.1.0", "fs-extra": "^7.0.1", @@ -1045,9 +1223,9 @@ } }, "node_modules/@changesets/get-dependents-graph/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1072,9 +1250,9 @@ } }, "node_modules/@changesets/get-github-info": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@changesets/get-github-info/-/get-github-info-0.5.2.tgz", - "integrity": "sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@changesets/get-github-info/-/get-github-info-0.6.0.tgz", + "integrity": "sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==", "dev": true, "dependencies": { "dataloader": "^1.4.0", @@ -1082,35 +1260,35 @@ } }, "node_modules/@changesets/get-release-plan": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.17.tgz", - "integrity": "sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.0.tgz", + "integrity": "sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/assemble-release-plan": "^5.2.4", - "@changesets/config": "^2.3.1", - "@changesets/pre": "^1.0.14", - "@changesets/read": "^0.5.9", - "@changesets/types": "^5.2.1", + "@changesets/assemble-release-plan": "^6.0.0", + "@changesets/config": "^3.0.0", + "@changesets/pre": "^2.0.0", + "@changesets/read": "^0.6.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3" } }, "node_modules/@changesets/get-version-range-type": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz", - "integrity": "sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", "dev": true }, "node_modules/@changesets/git": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-2.0.0.tgz", - "integrity": "sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.0.tgz", + "integrity": "sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.2", @@ -1118,9 +1296,9 @@ } }, "node_modules/@changesets/logger": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.0.5.tgz", - "integrity": "sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.0.tgz", + "integrity": "sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==", "dev": true, "dependencies": { "chalk": "^2.1.0" @@ -1162,39 +1340,39 @@ } }, "node_modules/@changesets/parse": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.16.tgz", - "integrity": "sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.0.tgz", + "integrity": "sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==", "dev": true, "dependencies": { - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.0.0", "js-yaml": "^3.13.1" } }, "node_modules/@changesets/pre": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.14.tgz", - "integrity": "sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.0.tgz", + "integrity": "sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/errors": "^0.1.4", - "@changesets/types": "^5.2.1", + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "node_modules/@changesets/read": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.5.9.tgz", - "integrity": "sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.0.tgz", + "integrity": "sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/git": "^2.0.0", - "@changesets/logger": "^0.0.5", - "@changesets/parse": "^0.3.16", - "@changesets/types": "^5.2.1", + "@changesets/git": "^3.0.0", + "@changesets/logger": "^0.1.0", + "@changesets/parse": "^0.4.0", + "@changesets/types": "^6.0.0", "chalk": "^2.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0" @@ -1236,19 +1414,19 @@ } }, "node_modules/@changesets/types": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-5.2.1.tgz", - "integrity": "sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.0.0.tgz", + "integrity": "sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==", "dev": true }, "node_modules/@changesets/write": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.2.3.tgz", - "integrity": "sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.3.0.tgz", + "integrity": "sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw==", "dev": true, "dependencies": { "@babel/runtime": "^7.20.1", - "@changesets/types": "^5.2.1", + "@changesets/types": "^6.0.0", "fs-extra": "^7.0.1", "human-id": "^1.0.2", "prettier": "^2.7.1" @@ -1276,32 +1454,384 @@ "dev": true, "optional": true, "engines": { - "node": ">=0.1.90" + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.7.tgz", - "integrity": "sha512-FVDOdfgyGOOISpd0b+UtA6YNbu5+RzZu7kDztjVsA/iZhGnyxbCR/vZ+B2j5yxbMZ9j3iz5uFiHIq1sl6nrZ0Q==", + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ - "arm64" + "x64" ], "dev": true, "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">=12" @@ -1323,18 +1853,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1361,9 +1891,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1400,21 +1930,21 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.0.tgz", - "integrity": "sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.3.tgz", + "integrity": "sha512-FeKv9lKLMwqDu0pQjPpF59GY3HReUkWXKsMIuMuJQOKh9BETu7zPEFUELvcw8w+lwZkl4ileJsHXC9+AnsT2Lw==", "dev": true, "dependencies": { - "@graphql-tools/utils": "^10.0.0", + "@graphql-tools/utils": "^10.0.13", "tslib": "^2.4.0" }, "engines": { @@ -1425,13 +1955,13 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz", - "integrity": "sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.3.tgz", + "integrity": "sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==", "dev": true, "dependencies": { - "@graphql-tools/merge": "^9.0.0", - "@graphql-tools/utils": "^10.0.0", + "@graphql-tools/merge": "^9.0.3", + "@graphql-tools/utils": "^10.0.13", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -1443,12 +1973,14 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.0.tgz", - "integrity": "sha512-ndBPc6zgR+eGU/jHLpuojrs61kYN3Z89JyMLwK3GCRkPv4EQn9EOr1UWqF1JO0iM+/jAVHY0mvfUxyrFFN9DUQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.2.0.tgz", + "integrity": "sha512-HYV7dO6pNA2nGKawygaBpk8y+vXOUjjzzO43W/Kb7EPRmXUEQKjHxPYRvQbiF72u1N3XxwGK5jnnFk9WVhUwYw==", "dev": true, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", + "cross-inspect": "1.0.0", + "dset": "^3.1.2", "tslib": "^2.4.0" }, "engines": { @@ -1467,13 +1999,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -1494,9 +2026,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1987,19 +2519,29 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", - "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -2010,9 +2552,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2111,37 +2653,50 @@ } }, "node_modules/@microsoft/api-extractor": { - "version": "7.38.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.38.0.tgz", - "integrity": "sha512-e1LhZYnfw+JEebuY2bzhw0imDCl1nwjSThTrQqBXl40hrVo6xm3j/1EpUr89QyzgjqmAwek2ZkIVZbrhaR+cqg==", + "version": "7.43.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.2.tgz", + "integrity": "sha512-5bVGdT/fHTDnBk6XPrw4I/U54LIvEeicOOTcyMtBWq387fad+m6tRk2cP/Lg9bz8+/gJgEkTVhpI8FXV4d79Ng==", "dev": true, "dependencies": { - "@microsoft/api-extractor-model": "7.28.2", + "@microsoft/api-extractor-model": "7.28.15", "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.61.0", - "@rushstack/rig-package": "0.5.1", - "@rushstack/ts-command-line": "4.16.1", - "colors": "~1.2.1", + "@rushstack/node-core-library": "4.2.0", + "@rushstack/rig-package": "0.5.2", + "@rushstack/terminal": "0.10.2", + "@rushstack/ts-command-line": "4.19.3", "lodash": "~4.17.15", + "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", - "typescript": "~5.0.4" + "typescript": "5.4.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "node_modules/@microsoft/api-extractor-model": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.2.tgz", - "integrity": "sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==", + "version": "7.28.15", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.15.tgz", + "integrity": "sha512-kAFX0c1+N+2WpZaiksy8H4RZ1sytJb2ZFVEmil5Rt6IK8UExU80f0/4kegXIs1KF8a/YyRW0Pybc7svlT9j/wQ==", "dev": true, "dependencies": { "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.61.0" + "@rushstack/node-core-library": "4.2.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/@microsoft/api-extractor/node_modules/semver": { @@ -2160,16 +2715,16 @@ } }, "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/@microsoft/tsdoc": { @@ -2292,12 +2847,11 @@ "dev": true }, "node_modules/@rushstack/node-core-library": { - "version": "3.61.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.61.0.tgz", - "integrity": "sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.2.0.tgz", + "integrity": "sha512-y2+m9bbkl1Xe5pt+8gouzRXtXoA2r7B2xkGDT4lpSCpiAU7HNHmhmqxOz+vTmoCamuTj1zqQbgyuoZ1z9cGdag==", "dev": true, "dependencies": { - "colors": "~1.2.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", @@ -2330,24 +2884,57 @@ } }, "node_modules/@rushstack/rig-package": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", - "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", + "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", "dev": true, "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, + "node_modules/@rushstack/terminal": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.2.tgz", + "integrity": "sha512-oMN4uoz6WUeLR9yWHSR4gEEii+8vjIJXPLp7U0k6zccgmOCJXYPKBK30FGpWfDRmqrcCIJi828SKV9V5FB1a0Q==", + "dev": true, + "dependencies": { + "@rushstack/node-core-library": "4.2.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/@rushstack/ts-command-line": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.16.1.tgz", - "integrity": "sha512-+OCsD553GYVLEmz12yiFjMOzuPeCiZ3f8wTiFHL30ZVXexTyPmgjwXEhg2K2P0a2lVf+8YBy7WtPoflB2Fp8/A==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.3.tgz", + "integrity": "sha512-gWJPWIlr1VC2byK3ZfXMoPLCNT6fFk4qXAb2x2deVRJpq/LQh03galWqissit8QCOS7mOJPyM42uWmT8f4MKRg==", "dev": true, "dependencies": { + "@rushstack/terminal": "0.10.2", "@types/argparse": "1.0.38", "argparse": "~1.0.9", - "colors": "~1.2.1", "string-argv": "~0.3.1" } }, @@ -2357,6 +2944,30 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", @@ -2376,96 +2987,94 @@ } }, "node_modules/@size-limit/esbuild": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@size-limit/esbuild/-/esbuild-8.2.6.tgz", - "integrity": "sha512-a4c8xVDuDMYw5jF655ADjQDluw3jGPPYer6UJock5rSnUlWnIbmT/Ohud7gJGq5gqyLUQOCrBD7NB3g+mlhj4g==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@size-limit/esbuild/-/esbuild-11.1.2.tgz", + "integrity": "sha512-IGQNaZsS4kP4hwU9C8P+3VvPhtW9PQ9OrwKJsvHDgMsbGX01hz39b9KkVwoI29wOXdwtj0aETaJUZPqoJq588w==", "dev": true, "dependencies": { - "esbuild": "^0.18.6", - "nanoid": "^3.3.6" + "esbuild": "^0.20.2", + "nanoid": "^5.0.6" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "size-limit": "8.2.6" + "size-limit": "11.1.2" } }, "node_modules/@size-limit/esbuild-why": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@size-limit/esbuild-why/-/esbuild-why-8.2.6.tgz", - "integrity": "sha512-Zd+24d5RKpB1ow//ICyDHq8BCx9dF5DZ3FyssYFwdZe/ljE1qpXfz8NpEOJCH4fDlSiTSsWmnWYW8VarfsiXEw==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@size-limit/esbuild-why/-/esbuild-why-11.1.2.tgz", + "integrity": "sha512-qNCLK2PxgBc2n+yVIdod5xl4LStMqx1HoqDR1cN72fbkUeZ1DTqWnfb/ytvcj3d/u7kHnjT46ythXO7WOVzYjg==", "dev": true, "dependencies": { - "esbuild-visualizer": "^0.4.0", - "open": "^8.4.2" + "esbuild-visualizer": "^0.6.0", + "open": "^10.1.0" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "size-limit": "8.2.6" + "size-limit": "11.1.2" } }, - "node_modules/@size-limit/esbuild-why/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "node_modules/@size-limit/esbuild-why/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@size-limit/file": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@size-limit/file/-/file-8.2.6.tgz", - "integrity": "sha512-B7ayjxiJsbtXdIIWazJkB5gezi5WBMecdHTFPMDhI3NwEML1RVvUjAkrb1mPAAkIpt2LVHPnhdCUHjqDdjugwg==", + "node_modules/@size-limit/esbuild-why/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, "dependencies": { - "semver": "7.5.3" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": ">=18" }, - "peerDependencies": { - "size-limit": "8.2.6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@size-limit/file/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/@size-limit/file": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@size-limit/file/-/file-11.1.2.tgz", + "integrity": "sha512-zktWwhO7MxVwQXbrZzy0VKfM5mZK3Aza1G3XbWRP8q+/3+irPKCz2fmyYJqJAJVwC9U1jAs6xEPlTJzxKgEAmw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "size-limit": "11.1.2" } }, "node_modules/@size-limit/preset-small-lib": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/@size-limit/preset-small-lib/-/preset-small-lib-8.2.6.tgz", - "integrity": "sha512-roanEuscDaaXDsT5Cg9agMbmsQVlMr66eRg3AwT2o4vE7WFLR8Z42p0AHZiwucW1nGpCxAh8E08Qa/yyVuj5nA==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@size-limit/preset-small-lib/-/preset-small-lib-11.1.2.tgz", + "integrity": "sha512-fxZW3woI6SOYyvq44QhlnYdcYfOfiW7zqFCPf+1Ox0OHbrug7YuMz74JNFlHJcnvB4Z6aErED+afkoYJ7vxohQ==", "dev": true, "dependencies": { - "@size-limit/esbuild": "8.2.6", - "@size-limit/file": "8.2.6", - "size-limit": "8.2.6" + "@size-limit/esbuild": "11.1.2", + "@size-limit/file": "11.1.2", + "size-limit": "11.1.2" }, "peerDependencies": { - "size-limit": "8.2.6" + "size-limit": "11.1.2" } }, "node_modules/@testing-library/dom": { @@ -2488,25 +3097,48 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", - "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz", + "integrity": "sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==", "dev": true, "dependencies": { - "@adobe/css-tools": "^4.0.1", + "@adobe/css-tools": "^4.3.2", "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", "redent": "^3.0.0" }, "engines": { - "node": ">=8", + "node": ">=14", "npm": ">=6", "yarn": ">=1" + }, + "peerDependencies": { + "@jest/globals": ">= 28", + "@types/bun": "latest", + "@types/jest": ">= 28", + "jest": ">= 28", + "vitest": ">= 0.32" + }, + "peerDependenciesMeta": { + "@jest/globals": { + "optional": true + }, + "@types/bun": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "jest": { + "optional": true + }, + "vitest": { + "optional": true + } } }, "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { @@ -2555,22 +3187,34 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, "node_modules/@testing-library/react": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.0.0.tgz", - "integrity": "sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==", + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.6.tgz", + "integrity": "sha512-UlbazRtEpQClFOiYp+1BapMT+xyqWMnE+hh9tn5DQ6gmlE7AIZWcGpzZukmDZuFk3By01oiqOf8lRedLS4k6xQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", + "@testing-library/dom": "^10.0.0", "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { + "@types/react": "^18.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@testing-library/react-12": { @@ -2593,54 +3237,54 @@ } }, "node_modules/@testing-library/react-12/node_modules/@types/react": { - "version": "17.0.47", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.47.tgz", - "integrity": "sha512-mk0BL8zBinf2ozNr3qPnlu1oyVTYq+4V7WA76RgxUAtf0Em/Wbid38KN6n4abEkvO4xMTBWmnP1FtQzgkEiJoA==", + "version": "17.0.80", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz", + "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==", "dev": true, "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", + "@types/scheduler": "^0.16", "csstype": "^3.0.2" } }, "node_modules/@testing-library/react-12/node_modules/@types/react-dom": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz", - "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==", + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.25.tgz", + "integrity": "sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==", "dev": true, "dependencies": { "@types/react": "^17" } }, "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.0.0.tgz", - "integrity": "sha512-+/TLgKNFsYUshOY/zXsQOk+PlFQK+eyJ9T13IDVNJEi+M+Un7xlJK+FZKkbGSnf0+7E1G6PlDhkSYQ/GFiruBQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz", + "integrity": "sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", + "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/react/node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true }, "node_modules/@testing-library/user-event": { - "version": "14.5.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz", - "integrity": "sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg==", + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", "dev": true, "engines": { "node": ">=12", @@ -2659,6 +3303,57 @@ "node": ">= 10" } }, + "node_modules/@ts-morph/common": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", + "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", @@ -2684,9 +3379,9 @@ "dev": true }, "node_modules/@tsconfig/node20": { - "version": "20.1.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz", - "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==", + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", + "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", "dev": true }, "node_modules/@types/argparse": { @@ -2743,15 +3438,15 @@ } }, "node_modules/@types/bytes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.3.tgz", - "integrity": "sha512-eEgZiWn6cjG8tc+AkI3FIa9ub9zhLMSRHqbecHe5yffqws+848zoHdbgFYxvUks4RElfJB9cupvqcd1gvDFQig==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.4.tgz", + "integrity": "sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA==", "dev": true }, "node_modules/@types/fetch-mock": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.3.7.tgz", - "integrity": "sha512-VKtaL2UUTFLXN+JoBOhF0elvQ5eu7ViSVAJDnogsDz4ygWV8/SaAKfJjFbZkHeV/jpAotObjitXM+VZcs+mwGQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.3.8.tgz", + "integrity": "sha512-ztsIGiyUvD0GaqPc9/hb8k20gnr6lupqA6SFtqt+8v2mtHhNO/Ebb6/b7N6af/7x0A7s1C8nxrEGzajMBqz8qA==", "dev": true }, "node_modules/@types/glob": { @@ -2774,24 +3469,15 @@ } }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz", - "integrity": "sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, - "node_modules/@types/is-ci": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/is-ci/-/is-ci-3.0.0.tgz", - "integrity": "sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.1.0" - } - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -2817,9 +3503,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.6", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.6.tgz", - "integrity": "sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2870,9 +3556,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -2882,9 +3568,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.200", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", - "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.1.tgz", + "integrity": "sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q==", "dev": true }, "node_modules/@types/minimatch": { @@ -2900,18 +3586,18 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.8.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", - "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "version": "20.12.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.10.tgz", + "integrity": "sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", "dev": true, "dependencies": { "@types/node": "*", @@ -2931,25 +3617,30 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.33", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", - "integrity": "sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", + "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "dev": true, "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", - "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "dependencies": { "@types/react": "*" } }, + "node_modules/@types/relay-runtime": { + "version": "14.1.23", + "resolved": "https://registry.npmjs.org/@types/relay-runtime/-/relay-runtime-14.1.23.tgz", + "integrity": "sha512-tP2l6YLI2HJ11UzEB7j4IWeADyiPIKTehdeyHsyOzNBu7WvKsyf4kAZDmsB2NPaXp9Lud+KEJbRi/VW+jEDYCA==", + "dev": true + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -2966,9 +3657,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -2977,15 +3668,6 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", - "dev": true, - "dependencies": { - "@types/jest": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -2993,9 +3675,9 @@ "dev": true }, "node_modules/@types/use-sync-external-store": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.5.tgz", - "integrity": "sha512-+fHc7rdrgMIng29ISUqNjsbPl1EMo1PCDh/+16HNlTOJeQzs6c9Om23rVizETd3dDx4YM+aWGbyF/KP4FUwZyg==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "dev": true }, "node_modules/@types/yargs": { @@ -3014,33 +3696,32 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz", - "integrity": "sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", + "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/type-utils": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/type-utils": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3048,42 +3729,28 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz", - "integrity": "sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3092,19 +3759,20 @@ } }, "node_modules/@typescript-eslint/rule-tester": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-6.7.5.tgz", - "integrity": "sha512-rDUQDkMg577jA7U+2NFlgLv3S43epbjruLZ4MQaELhH+WZLmmGylFbcCWeonRmtFjF2Xd/lfYsjqQ+p61XZDkw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-7.9.0.tgz", + "integrity": "sha512-Oz5IB1kCUToVS0Nvkpgx5Vb89J+7iSsBAWKvfxQFWmcYpiqdStO6eRI2cAuRNrqo6uGh2sNsAji9hcv/KrHwMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "ajv": "^6.10.0", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "ajv": "^6.12.6", "lodash.merge": "4.6.2", - "semver": "^7.5.4" + "semver": "^7.6.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3112,17 +3780,15 @@ }, "peerDependencies": { "@eslint/eslintrc": ">=2", - "eslint": ">=8" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/rule-tester/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3131,16 +3797,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz", - "integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", + "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5" + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3148,25 +3815,26 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz", - "integrity": "sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", + "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/utils": "6.7.5", + "@typescript-eslint/typescript-estree": "7.9.0", + "@typescript-eslint/utils": "7.9.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3175,12 +3843,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz", - "integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", "dev": true, + "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3188,21 +3857,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz", - "integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", + "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/visitor-keys": "7.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -3214,14 +3885,38 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3230,62 +3925,63 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.5.tgz", - "integrity": "sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/typescript-estree": "7.9.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz", - "integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", + "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.7.5", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.9.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@wry/caches": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.0.tgz", + "integrity": "sha512-FHRUDe2tqrXAj6A/1D39No68lFWbbnh+NCpG9J/6idhL/2Mb/AaxBTYg/sbUVImEo8a4mWeOewUlB1W7uLjByA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wry/context": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz", @@ -3309,9 +4005,9 @@ } }, "node_modules/@wry/trie": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", - "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", + "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", "dependencies": { "tslib": "^2.3.0" }, @@ -3332,9 +4028,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3497,12 +4193,25 @@ } }, "node_modules/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "engines": { - "node": ">=6.0" + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { @@ -3588,6 +4297,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -3597,18 +4327,6 @@ "node": ">=0.10.0" } }, - "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", - "dev": true, - "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" - } - }, "node_modules/ast-types": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", @@ -3637,10 +4355,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -3805,12 +4526,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3832,26 +4553,35 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/bs-logger": { @@ -3903,9 +4633,9 @@ } }, "node_modules/builtins/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3917,6 +4647,21 @@ "node": ">=10" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3936,13 +4681,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3984,14 +4730,24 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001298", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz", - "integrity": "sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==", + "version": "1.0.30001623", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001623.tgz", + "integrity": "sha512-X/XhAVKlpIxWPpgRTnlgZssJrF0m6YtRA0QDWgsBNT12uZM6LPRydR7ip405Y3t1LamD8cP2TZFEDZFBf5ApcA==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, "node_modules/cardinal": { "version": "2.1.1", @@ -4071,16 +4827,10 @@ "dev": true }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4093,6 +4843,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -4166,6 +4919,12 @@ "node": ">= 0.12.0" } }, + "node_modules/code-block-writer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.1.tgz", + "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==", + "dev": true + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -4187,15 +4946,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4230,9 +4980,9 @@ } }, "node_modules/core-js": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz", - "integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.35.0.tgz", + "integrity": "sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg==", "dev": true, "hasInstallScript": true, "funding": { @@ -4268,14 +5018,26 @@ "dev": true }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, "dependencies": { "node-fetch": "^2.6.12" } }, + "node_modules/cross-inspect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz", + "integrity": "sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4505,6 +5267,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -4517,21 +5307,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -4551,6 +5359,15 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -4647,6 +5464,15 @@ "node": ">=10" } }, + "node_modules/dset": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz", + "integrity": "sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4654,9 +5480,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.38", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.38.tgz", - "integrity": "sha512-WhHt3sZazKj0KK/UpgsbGQnUUoFeAHVishzHFExMxagpZgjiGYSC9S0ZlbhCfSH2L2i+2A1yyqOIliTctMx7KQ==", + "version": "1.4.783", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz", + "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==", "dev": true }, "node_modules/emittery": { @@ -4677,6 +5503,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -4724,35 +5556,50 @@ } }, "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", - "has": "^1.0.3", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -4761,6 +5608,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -4787,16 +5648,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "node_modules/esbuild": { - "version": "0.18.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.7.tgz", - "integrity": "sha512-46V0EFvQ/urmruUCChD1e0SZJWM0Ulny5F+uf5QkBry97HfvgvZTnjpTrwmw0+CGRhqTh9zpFeB+W8WGIEXOAQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, "bin": { @@ -4806,44 +5661,55 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.7", - "@esbuild/android-arm64": "0.18.7", - "@esbuild/android-x64": "0.18.7", - "@esbuild/darwin-arm64": "0.18.7", - "@esbuild/darwin-x64": "0.18.7", - "@esbuild/freebsd-arm64": "0.18.7", - "@esbuild/freebsd-x64": "0.18.7", - "@esbuild/linux-arm": "0.18.7", - "@esbuild/linux-arm64": "0.18.7", - "@esbuild/linux-ia32": "0.18.7", - "@esbuild/linux-loong64": "0.18.7", - "@esbuild/linux-mips64el": "0.18.7", - "@esbuild/linux-ppc64": "0.18.7", - "@esbuild/linux-riscv64": "0.18.7", - "@esbuild/linux-s390x": "0.18.7", - "@esbuild/linux-x64": "0.18.7", - "@esbuild/netbsd-x64": "0.18.7", - "@esbuild/openbsd-x64": "0.18.7", - "@esbuild/sunos-x64": "0.18.7", - "@esbuild/win32-arm64": "0.18.7", - "@esbuild/win32-ia32": "0.18.7", - "@esbuild/win32-x64": "0.18.7" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/esbuild-visualizer": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/esbuild-visualizer/-/esbuild-visualizer-0.4.0.tgz", - "integrity": "sha512-Sa1nXTxAPasvsyYKx/LVLgFy9CUY9rC4wGCkxjhB9+oL3bTYAlqAo2TcWaGS01T/KwG5qGOfyFWLTjjfokaAEA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/esbuild-visualizer/-/esbuild-visualizer-0.6.0.tgz", + "integrity": "sha512-oNK3JAhC7+re93VTtUdWJKTDVnA2qXPAjCAoaw9OxEFUXztszw3kcaK46u1U790T8FdUBAWv6F9Xt59P8nJCVA==", "dev": true, "dependencies": { "open": "^8.4.0", + "picomatch": "^2.3.1", "yargs": "^17.6.2" }, "bin": { "esbuild-visualizer": "dist/bin/cli.js" }, "engines": { - "node": ">=14.20" + "node": ">=18" + } + }, + "node_modules/esbuild-visualizer/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/esbuild-visualizer/node_modules/open": { @@ -4864,9 +5730,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -4904,18 +5770,19 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -5081,16 +5948,48 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-local-rules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.0.tgz", - "integrity": "sha512-sWueme0kUcP0JC1+6OBDQ9edBDVFJR92WJHSRbhiRExlenMEuUisdaVBPR+ItFBFXo2Pdw6FD2UfGZWkz8e93g==", - "dev": true - }, + "node_modules/eslint-plugin-local-rules": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.1.tgz", + "integrity": "sha512-AJhGd+GcI5r2dbjiGPixM8jnBl0XFxqoVbqzwKbYz+nTk+Cj5dNE3+OlhC176bl5r25KsGsIthLi1VqIW5Ga+A==", + "dev": true + }, + "node_modules/eslint-plugin-react-compiler": { + "version": "0.0.0-experimental-c8b3f72-20240517", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-0.0.0-experimental-c8b3f72-20240517.tgz", + "integrity": "sha512-cxUTFNMEKiLX6uFaRfrr2GHnB7KUHDMYLjEGzDec82ka6WyBCHg906nGSf3JvVnQKHaBDfUk7Mmv/JMvdgQB8Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "hermes-parser": "^0.20.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, "node_modules/eslint-plugin-testing-library": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", - "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.2.2.tgz", + "integrity": "sha512-1E94YOTUDnOjSLyvOwmbVDzQi/WkKm3WVrMXu6SmBr6DN95xTGZmI6HJ/eOkSXh/DlheRsxaPsJvZByDBhWLVQ==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.58.0" @@ -5226,9 +6125,9 @@ } }, "node_modules/eslint-plugin-testing-library/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5315,9 +6214,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5582,9 +6481,9 @@ } }, "node_modules/expect-type": { - "version": "0.17.3", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-0.17.3.tgz", - "integrity": "sha512-K0ZdZJ97jiAtaOwhEHHz/f0N6Xbj5reRz5g6+5BO7+OvqQ7PMQz0/c8bFSJs1zPotNJL5HJaC6t6lGPEAtGyOw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-0.19.0.tgz", + "integrity": "sha512-piv9wz3IrAG4Wnk2A+n2VRCHieAyOSxrRLU872Xo6nyn39kYXKDALk4OcqnvLRnFvkz659CnWC8MWZLuuQnoqg==", "dev": true, "engines": { "node": ">=12.0.0" @@ -5629,9 +6528,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5707,19 +6606,10 @@ } } }, - "node_modules/fetch-ponyfill": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-7.1.0.tgz", - "integrity": "sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==", - "dev": true, - "dependencies": { - "node-fetch": "~2.6.1" - } - }, "node_modules/fflate": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", - "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "dev": true }, "node_modules/file-entry-cache": { @@ -5735,9 +6625,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -5918,21 +6808,24 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -5969,14 +6862,15 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6098,6 +6992,21 @@ "node": ">=4" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -6172,10 +7081,13 @@ } }, "node_modules/graphql-ws": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.14.2.tgz", - "integrity": "sha512-LycmCwhZ+Op2GlHz4BZDsUYHKRiiUz+3r9wbhBATMETNlORQJAaFlAgTFoeRh6xQoQegwYwIylVD1Qns9/DA3w==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", + "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", "dev": true, + "workspaces": [ + "website" + ], "engines": { "node": ">=10" }, @@ -6234,6 +7146,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -6247,12 +7171,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -6261,6 +7185,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz", + "integrity": "sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==", + "dev": true + }, + "node_modules/hermes-parser": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz", + "integrity": "sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==", + "dev": true, + "dependencies": { + "hermes-estree": "0.20.1" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -6348,9 +7299,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -6444,30 +7395,28 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { "node": ">= 0.4" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6531,18 +7480,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -6612,21 +7549,6 @@ "node": ">=6" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6639,28 +7561,45 @@ "node": ">=0.10.0" } }, - "node_modules/is-module": { + "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -6791,7 +7730,7 @@ "node_modules/is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", "dev": true }, "node_modules/is-symbol": { @@ -6810,16 +7749,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -6861,6 +7796,12 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -7967,12 +8908,35 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "dev": true }, + "node_modules/js-cleanup": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", + "integrity": "sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==", + "dev": true, + "dependencies": { + "magic-string": "^0.25.7", + "perf-regexes": "^1.0.1", + "skip-regex": "^1.0.2" + }, + "engines": { + "node": "^10.14.2 || >=12.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8116,6 +9080,24 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json-stable-stringify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz", + "integrity": "sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -8149,6 +9131,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8199,12 +9190,15 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -8282,7 +9276,7 @@ "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, "node_modules/lodash.startcase": { @@ -8321,14 +9315,23 @@ "dev": true }, "node_modules/lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "bin": { "lz-string": "bin/bin.js" } }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8399,23 +9402,23 @@ } }, "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", + "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", "dev": true, "dependencies": { "ansi-escapes": "^6.2.0", "cardinal": "^2.1.1", - "chalk": "^5.2.0", + "chalk": "^5.3.0", "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" + "node-emoji": "^2.1.0", + "supports-hyperlinks": "^3.0.0" }, "engines": { - "node": ">=14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "marked": ">=1 <11" } }, "node_modules/marked-terminal/node_modules/ansi-escapes": { @@ -8434,9 +9437,9 @@ } }, "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -8446,9 +9449,9 @@ } }, "node_modules/marked-terminal/node_modules/type-fest": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.12.0.tgz", - "integrity": "sha512-qj9wWsnFvVEMUDbESiilKeXeHL7FwwiFcogfhfyjmvT968RXSvnl23f1JOClTHYItsi7o501C/7qVllscUP3oA==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { "node": ">=14.16" @@ -8646,9 +9649,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz", + "integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==", "dev": true, "funding": [ { @@ -8657,10 +9660,10 @@ } ], "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/nanospinner": { @@ -8679,12 +9682,18 @@ "dev": true }, "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", "dev": true, "dependencies": { - "lodash": "^4.17.21" + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/node-fetch": { @@ -8736,9 +9745,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/normalize-package-data": { @@ -8798,26 +9807,10 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8924,15 +9917,27 @@ } }, "node_modules/optimism": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.17.5.tgz", - "integrity": "sha512-TEcp8ZwK1RczmvMnvktxHSF2tKgMWjJ71xEFGX5ApLh67VsMSTy1ZUlipJw8W+KaqgOmQ+4pqwkeivY89j+4Vw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz", + "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==", "dependencies": { + "@wry/caches": "^1.0.0", "@wry/context": "^0.7.0", "@wry/trie": "^0.4.3", "tslib": "^2.3.0" } }, + "node_modules/optimism/node_modules/@wry/trie": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", + "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -9065,9 +10070,9 @@ } }, "node_modules/patch-package": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-7.0.2.tgz", - "integrity": "sha512-PMYfL8LXxGIRmxXLqlEaBxzKPu7/SdP13ld6GSfAUJUZRmBDPp8chZs0dpzaAFn9TSPnFiMwkC6PJt6pBiAl8Q==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", "dev": true, "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -9076,6 +10081,7 @@ "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", @@ -9185,6 +10191,12 @@ "node": ">= 10.0.0" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9258,16 +10270,25 @@ "node": ">=8" } }, + "node_modules/perf-regexes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", + "integrity": "sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==", + "dev": true, + "engines": { + "node": ">=6.14" + } + }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -9306,6 +10327,15 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/preferred-pm": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz", @@ -9392,9 +10422,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -9452,13 +10482,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/pseudomap": { @@ -9553,9 +10583,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "dependencies": { "loose-envify": "^1.1.0" @@ -9578,17 +10608,27 @@ "node": ">=0.10.0" } }, + "node_modules/react-19": { + "name": "react", + "version": "19.0.0-rc-cc1ec60d0d-20240607", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc-cc1ec60d0d-20240607.tgz", + "integrity": "sha512-q8A0/IdJ2wdHsjDNO1igFcSSFIMqSKmO7oJZtAjxIA9g0klK45Lxt15NQJ7z7cBvgD1r3xRTtQ/MAqnmwYHs1Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-dom-17": { @@ -9616,26 +10656,41 @@ "object-assign": "^4.1.1" } }, - "node_modules/react-error-boundary": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", - "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "node_modules/react-dom-19": { + "name": "react-dom", + "version": "19.0.0-rc-cc1ec60d0d-20240607", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc-cc1ec60d0d-20240607.tgz", + "integrity": "sha512-paspD9kAfKKuURVwKWJ0/g3qYw1DGi9h1k9xQV2iQN9cSVZ4JAOD727yjVLyp1zdzsoygjFfLMtSBdZ+oERYvA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5" + "scheduler": "0.25.0-rc-cc1ec60d0d-20240607" }, - "engines": { - "node": ">=10", - "npm": ">=6" + "peerDependencies": { + "react": "19.0.0-rc-cc1ec60d0d-20240607" + } + }, + "node_modules/react-dom-19/node_modules/scheduler": { + "version": "0.25.0-rc-cc1ec60d0d-20240607", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-cc1ec60d0d-20240607.tgz", + "integrity": "sha512-yFVKy6SDJkN2bOJSeH6gNo4+1MTygTZXnLRY5IHvEB6P9+O6WYRWz9PkELLjnl64lQwRgiigwzWQRSMNEboOGQ==", + "dev": true + }, + "node_modules/react-error-boundary": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", + "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" }, "peerDependencies": { "react": ">=16.13.1" } }, "node_modules/react-is": { - "version": "16.13.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", - "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==" + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/read-pkg": { "version": "5.2.0", @@ -9724,15 +10779,15 @@ } }, "node_modules/recast": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", - "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", + "version": "0.23.6", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.6.tgz", + "integrity": "sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==", "dev": true, "dependencies": { - "assert": "^2.0.0", "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" }, "engines": { @@ -9768,14 +10823,14 @@ "dev": true }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -9784,6 +10839,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9958,6 +11030,22 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-cleanup": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz", + "integrity": "sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==", + "dev": true, + "dependencies": { + "js-cleanup": "^1.2.0", + "rollup-pluginutils": "^2.8.2" + }, + "engines": { + "node": "^10.14.2 || >=12.0.0" + }, + "peerDependencies": { + "rollup": ">=2.0" + } + }, "node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -9987,6 +11075,33 @@ "node": ">= 10.13.0" } }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10019,6 +11134,24 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -10058,18 +11191,18 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dev": true, "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -10090,6 +11223,35 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10150,15 +11312,16 @@ "dev": true }, "node_modules/size-limit": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/size-limit/-/size-limit-8.2.6.tgz", - "integrity": "sha512-zpznim/tX/NegjoQuRKgWTF4XiB0cn2qt90uJzxYNTFAqexk4b94DOAkBD3TwhC6c3kw2r0KcnA5upziVMZqDg==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/size-limit/-/size-limit-11.1.2.tgz", + "integrity": "sha512-W9V/QR98fiLgGg+S77DNy7usExpz7HCdDAqm2t2Q77GWCV//wWUC6hyZA9QXKk1x6bxMMTzq1vmncw5Cve/43w==", "dev": true, "dependencies": { "bytes-iec": "^3.1.1", - "chokidar": "^3.5.3", - "globby": "^11.1.0", - "lilconfig": "^2.1.0", + "chokidar": "^3.6.0", + "globby": "^14.0.1", + "jiti": "^1.21.0", + "lilconfig": "^3.1.1", "nanospinner": "^1.1.0", "picocolors": "^1.0.0" }, @@ -10166,7 +11329,72 @@ "size-limit": "bin.js" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/size-limit/node_modules/globby": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", + "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/size-limit/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/size-limit/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/skip-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", + "integrity": "sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==", + "dev": true, + "engines": { + "node": ">=4.2" } }, "node_modules/slash": { @@ -10316,6 +11544,13 @@ "source-map": "^0.6.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, "node_modules/spawndamnit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-2.0.0.tgz", @@ -10505,29 +11740,46 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10638,16 +11890,16 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -10698,9 +11950,9 @@ } }, "node_modules/terser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", - "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -10755,6 +12007,12 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10821,7 +12079,7 @@ "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "dependencies": { "punycode": "^2.1.0" @@ -10837,17 +12095,23 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" } }, + "node_modules/ts-expose-internals-conditionally": { + "version": "1.0.0-empty.0", + "resolved": "https://registry.npmjs.org/ts-expose-internals-conditionally/-/ts-expose-internals-conditionally-1.0.0-empty.0.tgz", + "integrity": "sha512-F8m9NOF6ZhdOClDVdlM8gj3fDCav4ZIFSs/EI3ksQbAAXVSCN/Jh5OCJDDZWBuBy9psFc6jULGDlPwjMYMhJDw==", + "dev": true + }, "node_modules/ts-invariant": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", @@ -10860,9 +12124,9 @@ } }, "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -10878,7 +12142,7 @@ "ts-jest": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", @@ -10912,9 +12176,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -10926,10 +12190,20 @@ "node": ">=10" } }, + "node_modules/ts-morph": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", + "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.23.0", + "code-block-writer": "^13.0.1" + } + }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -10970,9 +12244,9 @@ } }, "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", "dev": true, "engines": { "node": ">=0.4.0" @@ -11100,6 +12374,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedoc": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.0.tgz", @@ -11146,9 +12485,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -11179,6 +12518,27 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -11188,6 +12548,36 @@ "node": ">= 4.0.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -11207,19 +12597,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -11366,9 +12743,9 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0.tgz", + "integrity": "sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw==", "dev": true, "engines": { "node": ">= 8" @@ -11393,9 +12770,9 @@ } }, "node_modules/whatwg-fetch": { - "version": "3.6.19", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz", - "integrity": "sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==", + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "dev": true }, "node_modules/whatwg-mimetype": { @@ -11469,17 +12846,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11773,6 +13149,27 @@ "dependencies": { "zen-observable": "0.8.15" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.3.0.tgz", + "integrity": "sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } } } } diff --git a/package.json b/package.json index 8511aa63782..a36db028e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apollo/client", - "version": "3.8.6", + "version": "3.10.4", "description": "A fully-featured caching GraphQL client.", "private": true, "keywords": [ @@ -32,7 +32,7 @@ "scripts": { "prebuild": "npm run clean", "build": "tsc", - "postbuild": "npm run update-version && npm run invariants && npm run sourcemaps && npm run rollup && npm run prepdist && npm run postprocess-dist && npm run verify-version", + "postbuild": "npm run update-version && npm run inline-inherit-doc && npm run invariants && npm run sourcemaps && npm run rollup && npm run prepdist && npm run postprocess-dist && npm run verify-version", "postinstall": "patch-package", "update-version": "node config/version.js update", "verify-version": "node config/version.js verify", @@ -42,15 +42,16 @@ "prepdist": "node ./config/prepareDist.js", "prepdist:changesets": "ts-node-script config/prepareChangesetsRelease.ts", "postprocess-dist": "ts-node-script config/postprocessDist.ts", - "extract-api": "ts-node-script config/apiExtractor.ts", + "extract-api": "npm run build && ts-node-script config/apiExtractor.ts --generate apiReport", "clean": "rimraf dist coverage lib temp", "check:format": "prettier --check .", "ci:precheck": "node config/precheck.js", "format": "prettier --write .", "lint": "eslint 'src/**/*.{[jt]s,[jt]sx}'", - "test": "jest --config ./config/jest.config.js", + "inline-inherit-doc": "ts-node-script config/inlineInheritDoc.ts", + "test": "node --expose-gc ./node_modules/jest/bin/jest.js --config ./config/jest.config.js", "test:debug": "node --inspect-brk node_modules/.bin/jest --config ./config/jest.config.js --runInBand --testTimeout 99999 --logHeapUsage", - "test:ci": "TEST_ENV=ci npm run test:coverage -- --logHeapUsage && npm run test:memory", + "test:ci": "TEST_ENV=ci npm run test:coverage -- --logHeapUsage", "test:watch": "jest --config ./config/jest.config.js --watch", "test:memory": "npm i && npm run build && cd scripts/memory && npm i && npm test", "test:coverage": "npm run coverage -- --ci --runInBand --reporters=default --reporters=jest-junit", @@ -59,15 +60,17 @@ "predeploy": "npm run build", "deploy": "cd dist && npm publish --tag next", "typedoc": "typedoc src/index.ts --json docs/public/docs.json", + "docmodel": "tsc && npm run inline-inherit-doc && ts-node-script config/apiExtractor.ts --main-only --generate docModel", "changeset-publish": "npm run build && npm run prepdist:changesets && cd dist && changeset publish", "changeset-check": "changeset status --verbose --since=origin/main", - "changeset-version": "changeset version && npm i" + "changeset-version": "changeset version && npm i", + "update-size-limits": "size-limit --json | jq '. | map(select(.sizeLimit) | { key: .name, value: .size}) | from_entries' | tee .new-size-limits.json; mv .new-size-limits.json .size-limits.json" }, "engines": { - "npm": "^7.20.3 || ^8.0.0 || ^9.0.0" + "npm": "^7.20.3 || ^8.0.0 || ^9.0.0 || ^10.0.0" }, "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql": "^15.0.0 || ^16.0.0", "graphql-ws": "^5.5.5", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -89,13 +92,14 @@ }, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", - "@wry/context": "^0.7.3", + "@wry/caches": "^1.0.0", "@wry/equality": "^0.5.6", - "@wry/trie": "^0.4.3", + "@wry/trie": "^0.5.0", "graphql-tag": "^2.12.6", "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.17.5", + "optimism": "^0.18.0", "prop-types": "^15.7.2", + "rehackt": "^0.1.0", "response-iterator": "^0.2.6", "symbol-observable": "^4.0.0", "ts-invariant": "^0.10.3", @@ -103,81 +107,100 @@ "zen-observable-ts": "^1.2.5" }, "devDependencies": { - "@arethetypeswrong/cli": "0.12.2", - "@babel/parser": "7.23.0", - "@changesets/changelog-github": "0.4.8", - "@changesets/cli": "2.26.2", - "@graphql-tools/schema": "10.0.0", - "@microsoft/api-extractor": "7.38.0", + "@arethetypeswrong/cli": "0.15.3", + "@babel/parser": "7.24.5", + "@changesets/changelog-github": "0.5.0", + "@changesets/cli": "2.27.1", + "@graphql-tools/merge": "9.0.3", + "@graphql-tools/schema": "10.0.3", + "@graphql-tools/utils": "10.2.0", + "@microsoft/api-extractor": "7.43.2", "@rollup/plugin-node-resolve": "11.2.1", - "@size-limit/esbuild-why": "8.2.6", - "@size-limit/preset-small-lib": "8.2.6", - "@testing-library/jest-dom": "5.17.0", - "@testing-library/react": "14.0.0", + "@size-limit/esbuild-why": "11.1.2", + "@size-limit/preset-small-lib": "11.1.2", + "@testing-library/jest-dom": "6.4.5", + "@testing-library/react": "15.0.6", "@testing-library/react-12": "npm:@testing-library/react@^12", - "@testing-library/user-event": "14.5.1", - "@tsconfig/node20": "20.1.2", - "@types/bytes": "3.1.3", - "@types/fetch-mock": "7.3.7", + "@testing-library/user-event": "14.5.2", + "@tsconfig/node20": "20.1.4", + "@types/bytes": "3.1.4", + "@types/fetch-mock": "7.3.8", "@types/glob": "8.1.0", - "@types/hoist-non-react-statics": "3.3.4", - "@types/jest": "29.5.6", - "@types/lodash": "4.14.200", - "@types/node": "20.8.9", - "@types/node-fetch": "2.6.7", - "@types/react": "18.2.33", - "@types/react-dom": "18.2.14", - "@types/use-sync-external-store": "0.0.5", - "@typescript-eslint/eslint-plugin": "6.7.5", - "@typescript-eslint/parser": "6.7.5", - "@typescript-eslint/rule-tester": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "acorn": "8.10.0", + "@types/hoist-non-react-statics": "3.3.5", + "@types/jest": "29.5.12", + "@types/lodash": "4.17.1", + "@types/node": "20.12.10", + "@types/node-fetch": "2.6.11", + "@types/react": "18.3.1", + "@types/react-dom": "18.3.0", + "@types/relay-runtime": "14.1.23", + "@types/use-sync-external-store": "0.0.6", + "@typescript-eslint/eslint-plugin": "7.9.0", + "@typescript-eslint/parser": "7.9.0", + "@typescript-eslint/rule-tester": "7.9.0", + "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/utils": "7.9.0", + "acorn": "8.11.3", "blob-polyfill": "7.0.20220408", "bytes": "3.1.2", - "cross-fetch": "3.1.8", - "eslint": "8.51.0", + "cross-fetch": "4.0.0", + "eslint": "8.57.0", "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "npm:@phryneas/eslint-plugin-import@2.27.5-pr.2813.2817.199971c", - "eslint-plugin-local-rules": "2.0.0", - "eslint-plugin-testing-library": "5.11.1", - "expect-type": "0.17.3", + "eslint-plugin-local-rules": "2.0.1", + "eslint-plugin-react-compiler": "0.0.0-experimental-c8b3f72-20240517", + "eslint-plugin-react-hooks": "4.6.2", + "eslint-plugin-testing-library": "6.2.2", + "expect-type": "0.19.0", "fetch-mock": "9.11.0", "glob": "8.1.0", "graphql": "16.8.1", - "graphql-ws": "5.14.2", + "graphql-ws": "5.16.0", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", "jest-junit": "16.0.0", "lodash": "4.17.21", - "patch-package": "7.0.2", - "prettier": "3.0.3", - "react": "18.2.0", + "patch-package": "8.0.0", + "prettier": "3.1.1", + "react": "18.3.1", "react-17": "npm:react@^17", - "react-dom": "18.2.0", + "react-19": "npm:react@19.0.0-rc-cc1ec60d0d-20240607", + "react-dom": "18.3.1", "react-dom-17": "npm:react-dom@^17", - "react-error-boundary": "3.1.4", - "recast": "0.23.4", + "react-dom-19": "npm:react-dom@19.0.0-rc-cc1ec60d0d-20240607", + "react-error-boundary": "4.0.13", + "recast": "0.23.6", "resolve": "1.22.8", "rimraf": "5.0.5", "rollup": "2.79.1", + "rollup-plugin-cleanup": "3.2.1", "rollup-plugin-terser": "7.0.2", "rxjs": "7.8.1", - "size-limit": "8.2.6", + "size-limit": "11.1.2", "subscriptions-transport-ws": "0.11.0", - "terser": "5.21.0", - "ts-api-utils": "1.0.3", - "ts-jest": "29.1.1", + "terser": "5.31.0", + "ts-api-utils": "1.3.0", + "ts-jest": "29.1.2", "ts-jest-resolver": "2.0.1", - "ts-node": "10.9.1", + "ts-morph": "22.0.0", + "ts-node": "10.9.2", "typedoc": "0.25.0", - "typescript": "5.2.2", + "typescript": "5.4.5", "wait-for-observables": "1.0.3", - "web-streams-polyfill": "3.2.1", - "whatwg-fetch": "3.6.19" + "web-streams-polyfill": "4.0.0", + "whatwg-fetch": "3.6.20" }, "publishConfig": { "access": "public" - } + }, + "files": [ + "LICENSE", + "**/*.md", + "**/*.cjs", + "**/*.cjs.map", + "**/*.js", + "**/*.js.map", + "**/*.d.ts", + "**/*.json" + ] } diff --git a/patches/eslint-plugin-react-hooks+4.6.2.patch b/patches/eslint-plugin-react-hooks+4.6.2.patch new file mode 100644 index 00000000000..a8356f0f3df --- /dev/null +++ b/patches/eslint-plugin-react-hooks+4.6.2.patch @@ -0,0 +1,28 @@ +diff --git a/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js b/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js +index 441442f..d1ec5dc 100644 +--- a/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js ++++ b/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js +@@ -905,7 +905,7 @@ var ExhaustiveDeps = { + var _callee = callee, + name = _callee.name; + +- if (name === 'useRef' && id.type === 'Identifier') { ++ if ((name === 'useRef' || name === "useLazyRef") && id.type === 'Identifier') { + // useRef() return value is stable. + return true; + } else if (name === 'useState' || name === 'useReducer') { +diff --git a/node_modules/eslint-plugin-react-hooks/index.js b/node_modules/eslint-plugin-react-hooks/index.js +index 0e91baf..7e86d46 100644 +--- a/node_modules/eslint-plugin-react-hooks/index.js ++++ b/node_modules/eslint-plugin-react-hooks/index.js +@@ -1,9 +1,3 @@ + 'use strict'; + +-// TODO: this doesn't make sense for an ESLint rule. +-// We need to fix our build process to not create bundles for "raw" packages like this. +-if (process.env.NODE_ENV === 'production') { +- module.exports = require('./cjs/eslint-plugin-react-hooks.production.min.js'); +-} else { +- module.exports = require('./cjs/eslint-plugin-react-hooks.development.js'); +-} ++module.exports = require('./cjs/eslint-plugin-react-hooks.development.js'); diff --git a/renovate.json b/renovate.json index ac7f81ee4b8..a9b8a73e6ed 100644 --- a/renovate.json +++ b/renovate.json @@ -34,6 +34,7 @@ "@testing-library/react-12", "@rollup/plugin-node-resolve", "rollup", - "glob" + "glob", + "prettier" ] } diff --git a/scripts/codemods/ac2-to-ac3/imports.js b/scripts/codemods/ac2-to-ac3/imports.js index e21dc594a6e..6ced8fff06f 100644 --- a/scripts/codemods/ac2-to-ac3/imports.js +++ b/scripts/codemods/ac2-to-ac3/imports.js @@ -103,8 +103,8 @@ export default function transformer(file, api) { function renameDefaultSpecifier(moduleImport, name) { function replacer(path) { - return path.value.local.name === name - ? j.importSpecifier(j.identifier(name)) + return path.value.local.name === name ? + j.importSpecifier(j.identifier(name)) : j.importSpecifier(j.identifier(name), path.value.local); } diff --git a/scripts/codemods/ac2-to-ac3/package.json b/scripts/codemods/ac2-to-ac3/package.json index 2f5fd7c3d30..10edeac9d56 100644 --- a/scripts/codemods/ac2-to-ac3/package.json +++ b/scripts/codemods/ac2-to-ac3/package.json @@ -1,6 +1,6 @@ { "private": true, "devDependencies": { - "jscodeshift": "0.15.1" + "jscodeshift": "0.15.2" } } diff --git a/scripts/memory/package.json b/scripts/memory/package.json index 1286437c971..e637bb89e7b 100644 --- a/scripts/memory/package.json +++ b/scripts/memory/package.json @@ -9,6 +9,6 @@ "graphql": "^16.0.0" }, "devDependencies": { - "mocha": "10.2.0" + "mocha": "10.4.0" } } diff --git a/src/__tests__/ApolloClient.ts b/src/__tests__/ApolloClient.ts index 50c668f934c..8ba8ccf7ab5 100644 --- a/src/__tests__/ApolloClient.ts +++ b/src/__tests__/ApolloClient.ts @@ -4,7 +4,6 @@ import { ApolloClient, ApolloError, DefaultOptions, - FetchPolicy, QueryOptions, makeReference, } from "../core"; @@ -15,7 +14,7 @@ import { ApolloLink } from "../link/core"; import { HttpLink } from "../link/http"; import { InMemoryCache } from "../cache"; import { itAsync } from "../testing"; -import { spyOnConsole } from "../testing/internal"; +import { ObservableStream, spyOnConsole } from "../testing/internal"; import { TypedDocumentNode } from "@graphql-typed-document-node/core"; import { invariant } from "../utilities/globals"; @@ -2126,8 +2125,8 @@ describe("ApolloClient", () => { } `; - ["network-only", "cache-and-network"].forEach( - (fetchPolicy: FetchPolicy) => { + (["network-only", "cache-and-network"] as const).forEach( + (fetchPolicy) => { const observable = client.watchQuery({ query, fetchPolicy, @@ -2156,13 +2155,15 @@ describe("ApolloClient", () => { } `; - [ - "cache-first", - "cache-and-network", - "network-only", - "cache-only", - "no-cache", - ].forEach((fetchPolicy: FetchPolicy) => { + ( + [ + "cache-first", + "cache-and-network", + "network-only", + "cache-only", + "no-cache", + ] as const + ).forEach((fetchPolicy) => { const observable = client.watchQuery({ query, fetchPolicy, @@ -2173,6 +2174,253 @@ describe("ApolloClient", () => { ); }); + describe("watchFragment", () => { + it("if all data is available, `complete` is `true`", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + const ItemFragment = gql` + fragment ItemFragment on Item { + id + text + } + `; + + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + }); + + const observable = client.watchFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 5 }, + }); + + const stream = new ObservableStream(observable); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + complete: true, + }); + } + }); + it("cache writes emit a new value", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + const ItemFragment = gql` + fragment ItemFragment on Item { + id + text + } + `; + + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + }); + + const observable = client.watchFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 5 }, + }); + + const stream = new ObservableStream(observable); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + complete: true, + }); + } + + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + text: "Item #5 (edited)", + }, + }); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + data: { + __typename: "Item", + id: 5, + text: "Item #5 (edited)", + }, + complete: true, + }); + } + }); + it("if only partial data is available, `complete` is `false`", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + const ItemFragment = gql` + fragment ItemFragment on Item { + id + text + } + `; + + { + // we expect a "Missing field 'text' while writing result..." error + // when writing item to the cache, so we'll silence the console.error + using _consoleSpy = spyOnConsole("error"); + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + }, + }); + } + + const observable = client.watchFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 5 }, + }); + + const stream = new ObservableStream(observable); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + data: { + __typename: "Item", + id: 5, + }, + complete: false, + missing: { + text: "Can't find field 'text' on Item:5 object", + }, + }); + } + }); + it("if no data is written after observable is subscribed to, next is never called", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + const ItemFragment = gql` + fragment ItemFragment on Item { + id + text + } + `; + + const observable = client.watchFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 5 }, + }); + + const stream = new ObservableStream(observable); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + complete: false, + data: {}, + missing: "Dangling reference to missing Item:5 object", + }); + } + + await expect(stream.takeNext({ timeout: 1000 })).rejects.toEqual( + expect.any(Error) + ); + }); + + it("supports the @nonreactive directive", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + const ItemFragment = gql` + fragment ItemFragment on Item { + id + text @nonreactive + } + `; + + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + }); + + const observable = client.watchFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 5 }, + }); + + const stream = new ObservableStream(observable); + + { + const result = await stream.takeNext(); + + expect(result).toEqual({ + data: { + __typename: "Item", + id: 5, + text: "Item #5", + }, + complete: true, + }); + } + + cache.writeFragment({ + fragment: ItemFragment, + data: { + __typename: "Item", + id: 5, + text: "Item #5 (edited)", + }, + }); + + await expect(stream.takeNext()).rejects.toThrow( + new Error("Timeout waiting for next event") + ); + }); + }); + describe("defaultOptions", () => { it( "should set `defaultOptions` to an empty object if not provided in " + diff --git a/src/__tests__/__snapshots__/exports.ts.snap b/src/__tests__/__snapshots__/exports.ts.snap index 6c06e405fd9..d3ce1568654 100644 --- a/src/__tests__/__snapshots__/exports.ts.snap +++ b/src/__tests__/__snapshots__/exports.ts.snap @@ -20,6 +20,7 @@ Array [ "checkFetcher", "concat", "createHttpLink", + "createQueryPreloader", "createSignalIfSupported", "defaultDataIdFromObject", "defaultPrinter", @@ -59,8 +60,10 @@ Array [ "useBackgroundQuery", "useFragment", "useLazyQuery", + "useLoadableQuery", "useMutation", "useQuery", + "useQueryRefHandlers", "useReactiveVar", "useReadQuery", "useSubscription", @@ -142,6 +145,7 @@ Array [ "loadDevMessages", "loadErrorMessageHandler", "loadErrorMessages", + "setErrorMessageHandler", ] `; @@ -264,6 +268,7 @@ Array [ "ApolloConsumer", "ApolloProvider", "DocumentType", + "createQueryPreloader", "getApolloContext", "operationName", "parser", @@ -273,8 +278,10 @@ Array [ "useBackgroundQuery", "useFragment", "useLazyQuery", + "useLoadableQuery", "useMutation", "useQuery", + "useQueryRefHandlers", "useReactiveVar", "useReadQuery", "useSubscription", @@ -316,8 +323,10 @@ Array [ "useBackgroundQuery", "useFragment", "useLazyQuery", + "useLoadableQuery", "useMutation", "useQuery", + "useQueryRefHandlers", "useReactiveVar", "useReadQuery", "useSubscription", @@ -325,6 +334,18 @@ Array [ ] `; +exports[`exports of public entry points @apollo/client/react/internal 1`] = ` +Array [ + "InternalQueryReference", + "assertWrappedQueryRef", + "getSuspenseCache", + "getWrappedPromise", + "unwrapQueryRef", + "updateWrappedQueryRef", + "wrapQueryRef", +] +`; + exports[`exports of public entry points @apollo/client/react/parser 1`] = ` Array [ "DocumentType", @@ -378,8 +399,17 @@ Array [ ] `; +exports[`exports of public entry points @apollo/client/testing/experimental 1`] = ` +Array [ + "createSchemaFetch", + "createTestSchema", +] +`; + exports[`exports of public entry points @apollo/client/utilities 1`] = ` Array [ + "AutoCleanedStrongCache", + "AutoCleanedWeakCache", "Concast", "DEV", "DeepMerger", @@ -389,12 +419,14 @@ Array [ "argumentsObjectFromField", "asyncMap", "buildQueryFromSelectionSet", + "cacheSizes", "canUseAsyncIteratorSymbol", "canUseDOM", "canUseLayoutEffect", "canUseSymbol", "canUseWeakMap", "canUseWeakSet", + "canonicalStringify", "checkDocument", "cloneDeep", "compact", @@ -477,3 +509,9 @@ Array [ "newInvariantError", ] `; + +exports[`exports of public entry points @apollo/client/utilities/subscriptions/urql 1`] = ` +Array [ + "createFetchMultipartSubscription", +] +`; diff --git a/src/__tests__/client.ts b/src/__tests__/client.ts index eb13692d37b..cf8df19c268 100644 --- a/src/__tests__/client.ts +++ b/src/__tests__/client.ts @@ -1821,7 +1821,7 @@ describe("client", () => { link, cache: new InMemoryCache({ - dataIdFromObject: (obj: { id: any }) => obj.id, + dataIdFromObject: (obj: any) => obj.id, addTypename: false, }), }); @@ -1870,7 +1870,7 @@ describe("client", () => { callback(); throw new Error("not reached"); } catch (thrown) { - expect(thrown.message).toBe(cacheAndNetworkError); + expect((thrown as Error).message).toBe(cacheAndNetworkError); } } @@ -2686,6 +2686,35 @@ describe("client", () => { spy.mockRestore(); }); + // See https://github.com/apollographql/apollo-client/issues/10238 + it("does not call QueryManager.refetchQueries for mutations with no-cache policy", async () => { + const mutation = gql` + mutation { + noop + } + `; + const link = mockSingleLink({ + request: { query: mutation }, + result: { data: { noop: false } }, + }); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const spy = jest.spyOn(client["queryManager"], "refetchQueries"); + spy.mockImplementation(() => new Map()); + + await client.mutate({ + mutation, + fetchPolicy: "no-cache", + }); + + expect(spy).not.toHaveBeenCalled(); + spy.mockRestore(); + }); + it("has a getObservableQueries method which calls QueryManager", async () => { const client = new ApolloClient({ link: ApolloLink.empty(), diff --git a/src/__tests__/exports.ts b/src/__tests__/exports.ts index 819ec6d2c81..d50b933810c 100644 --- a/src/__tests__/exports.ts +++ b/src/__tests__/exports.ts @@ -26,12 +26,15 @@ import * as reactComponents from "../react/components"; import * as reactContext from "../react/context"; import * as reactHOC from "../react/hoc"; import * as reactHooks from "../react/hooks"; +import * as reactInternal from "../react/internal"; import * as reactParser from "../react/parser"; import * as reactSSR from "../react/ssr"; import * as testing from "../testing"; import * as testingCore from "../testing/core"; +import * as testingExperimental from "../testing/experimental"; import * as utilities from "../utilities"; import * as utilitiesGlobals from "../utilities/globals"; +import * as urqlUtilities from "../utilities/subscriptions/urql"; const entryPoints = require("../../config/entryPoints.js"); @@ -70,17 +73,25 @@ describe("exports of public entry points", () => { check("@apollo/client/react/context", reactContext); check("@apollo/client/react/hoc", reactHOC); check("@apollo/client/react/hooks", reactHooks); + check("@apollo/client/react/internal", reactInternal); check("@apollo/client/react/parser", reactParser); check("@apollo/client/react/ssr", reactSSR); check("@apollo/client/testing", testing); check("@apollo/client/testing/core", testingCore); + check("@apollo/client/testing/experimental", testingExperimental); check("@apollo/client/utilities", utilities); check("@apollo/client/utilities/globals", utilitiesGlobals); + check("@apollo/client/utilities/subscriptions/urql", urqlUtilities); it("completeness", () => { const { join } = require("path").posix; entryPoints.forEach((info: Record) => { const id = join("@apollo/client", ...info.dirs); + // We don't want to add a devDependency for relay-runtime, + // and our API extractor job is already validating its public exports, + // so we'll skip the utilities/subscriptions/relay entrypoing here + // since it errors on the `relay-runtime` import. + if (id === "@apollo/client/utilities/subscriptions/relay") return; expect(testedIds).toContain(id); }); }); diff --git a/src/__tests__/graphqlSubscriptions.ts b/src/__tests__/graphqlSubscriptions.ts index 8b46eb01c26..2f4d66228d9 100644 --- a/src/__tests__/graphqlSubscriptions.ts +++ b/src/__tests__/graphqlSubscriptions.ts @@ -488,7 +488,7 @@ describe("GraphQL Subscriptions", () => { client.subscribe(options).subscribe({ next() { - expect(link.operation.getContext().someVar).toEqual( + expect(link.operation?.getContext().someVar).toEqual( options.context.someVar ); resolve(); diff --git a/src/__tests__/local-state/general.ts b/src/__tests__/local-state/general.ts index 30d31feea33..0d65993e821 100644 --- a/src/__tests__/local-state/general.ts +++ b/src/__tests__/local-state/general.ts @@ -1207,7 +1207,7 @@ describe("Combining client and server state/operations", () => { watchCount += 1; client.mutate({ mutation, - update(proxy, { data: { updateUser } }: { data: any }) { + update(proxy, { data: { updateUser } }) { proxy.writeQuery({ query: userQuery, data: { diff --git a/src/__tests__/local-state/resolvers.ts b/src/__tests__/local-state/resolvers.ts index c1052748841..b305a3ed7d8 100644 --- a/src/__tests__/local-state/resolvers.ts +++ b/src/__tests__/local-state/resolvers.ts @@ -11,7 +11,7 @@ import { WatchQueryOptions, } from "../../core"; -import { InMemoryCache } from "../../cache"; +import { InMemoryCache, isReference } from "../../cache"; import { Observable, Observer } from "../../utilities"; import { ApolloLink } from "../../link/core"; import { itAsync } from "../../testing"; @@ -747,10 +747,13 @@ describe("Writing cache data from resolvers", () => { }, }, }); - cache.modify({ + cache.modify<{ field: { field2: number } }>({ id: "Object:uniqueId", fields: { - field(value: { field2: number }) { + field(value) { + if (isReference(value)) { + fail("Should not be a reference"); + } expect(value.field2).toBe(1); return { ...value, field2: 2 }; }, diff --git a/src/__tests__/mutationResults.ts b/src/__tests__/mutationResults.ts index d7656a9232d..7f140dc17a0 100644 --- a/src/__tests__/mutationResults.ts +++ b/src/__tests__/mutationResults.ts @@ -2,7 +2,7 @@ import { cloneDeep } from "lodash"; import gql from "graphql-tag"; import { GraphQLError } from "graphql"; -import { ApolloClient } from "../core"; +import { ApolloClient, FetchResult } from "../core"; import { InMemoryCache } from "../cache"; import { ApolloLink } from "../link/core"; import { @@ -1186,8 +1186,6 @@ describe("mutation results", () => { subscribeAndCount(reject, watchedQuery, (count, result) => { if (count === 1) { - expect(result.data).toEqual({ echo: "a" }); - } else if (count === 2) { expect(result.data).toEqual({ echo: "b" }); client.mutate({ mutation: resetMutation, @@ -1197,7 +1195,7 @@ describe("mutation results", () => { }, }, }); - } else if (count === 3) { + } else if (count === 2) { expect(result.data).toEqual({ echo: "0" }); resolve(); } @@ -1821,5 +1819,40 @@ describe("mutation results", () => { }, reject); } ); + + itAsync( + "data might be undefined in case of failure with errorPolicy = ignore", + async (resolve, reject) => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink( + () => + new Observable>((observer) => { + observer.next({ + errors: [new GraphQLError("Oops")], + }); + observer.complete(); + }) + ).setOnError(reject), + }); + + const ignoreErrorsResult = await client.mutate({ + mutation: gql` + mutation Foo { + foo + } + `, + fetchPolicy: "no-cache", + errorPolicy: "ignore", + }); + + expect(ignoreErrorsResult).toEqual({ + data: undefined, + errors: undefined, + }); + + resolve(); + } + ); }); }); diff --git a/src/__tests__/optimistic.ts b/src/__tests__/optimistic.ts index d8c20501821..3cc984868ed 100644 --- a/src/__tests__/optimistic.ts +++ b/src/__tests__/optimistic.ts @@ -235,7 +235,7 @@ describe("optimistic mutation results", () => { await promise; } catch (err) { expect(err).toBeInstanceOf(Error); - expect(err.message).toBe("forbidden (test error)"); + expect((err as Error).message).toBe("forbidden (test error)"); const dataInStore = (client.cache as InMemoryCache).extract(true); expect((dataInStore["TodoList5"] as any).todos.length).toBe(3); @@ -489,7 +489,7 @@ describe("optimistic mutation results", () => { await promise; } catch (err) { expect(err).toBeInstanceOf(Error); - expect(err.message).toBe("forbidden (test error)"); + expect((err as Error).message).toBe("forbidden (test error)"); const dataInStore = (client.cache as InMemoryCache).extract(true); expect((dataInStore["TodoList5"] as any).todos.length).toBe(3); @@ -982,6 +982,113 @@ describe("optimistic mutation results", () => { resolve(); } ); + + itAsync( + "will not update optimistically if optimisticResponse returns IGNORE sentinel object", + async (resolve, reject) => { + expect.assertions(5); + + let subscriptionHandle: Subscription; + + const client = await setup(reject, { + request: { query: mutation, variables }, + result: mutationResult, + }); + + // we have to actually subscribe to the query to be able to update it + await new Promise((resolve) => { + const handle = client.watchQuery({ query }); + subscriptionHandle = handle.subscribe({ + next(res: any) { + resolve(res); + }, + }); + }); + + const id = "TodoList5"; + const isTodoList = ( + list: unknown + ): list is { todos: { text: string }[] } => + typeof initialList === "object" && + initialList !== null && + "todos" in initialList && + Array.isArray(initialList.todos); + + const initialList = client.cache.extract(true)[id]; + + if (!isTodoList(initialList)) { + reject(new Error("Expected TodoList")); + return; + } + + expect(initialList.todos.length).toEqual(3); + + const promise = client.mutate({ + mutation, + variables, + optimisticResponse: (vars, { IGNORE }) => { + return IGNORE; + }, + update: (proxy: any, mResult: any) => { + expect(mResult.data.createTodo.id).toBe("99"); + + const fragment = gql` + fragment todoList on TodoList { + todos { + id + text + completed + __typename + } + } + `; + + const data: any = proxy.readFragment({ id, fragment }); + + proxy.writeFragment({ + data: { + ...data, + todos: [mResult.data.createTodo, ...data.todos], + }, + id, + fragment, + }); + }, + }); + + const list = client.cache.extract(true)[id]; + + if (!isTodoList(list)) { + reject(new Error("Expected TodoList")); + return; + } + + expect(list.todos.length).toEqual(3); + + await promise; + + const result = await client.query({ query }); + + subscriptionHandle!.unsubscribe(); + + const newList = result.data.todoList; + + if (!isTodoList(newList)) { + reject(new Error("Expected TodoList")); + return; + } + + // There should be one more todo item than before + expect(newList.todos.length).toEqual(4); + + // Since we used `prepend` it should be at the front + expect(newList.todos[0].text).toBe( + "This one was created with a mutation." + ); + + resolve(); + } + ); }); describe("optimistic updates using `updateQueries`", () => { @@ -2019,11 +2126,12 @@ describe("optimistic mutation results", () => { const wrapReject = ( fn: (...args: TArgs) => TResult ): typeof fn => { - return function () { + return function (this: unknown, ...args: TArgs) { try { - return fn.apply(this, arguments); + return fn.apply(this, args); } catch (e) { reject(e); + throw e; } }; }; diff --git a/src/__tests__/subscribeToMore.ts b/src/__tests__/subscribeToMore.ts index 2903dacdbbd..419b0b696d0 100644 --- a/src/__tests__/subscribeToMore.ts +++ b/src/__tests__/subscribeToMore.ts @@ -8,8 +8,10 @@ import { itAsync, mockSingleLink, mockObservableLink } from "../testing"; const isSub = (operation: Operation) => (operation.query as DocumentNode).definitions - .filter((x) => x.kind === "OperationDefinition") - .some((x: OperationDefinitionNode) => x.operation === "subscription"); + .filter( + (x): x is OperationDefinitionNode => x.kind === "OperationDefinition" + ) + .some((x) => x.operation === "subscription"); describe("subscribeToMore", () => { const query = gql` diff --git a/src/cache/core/cache.ts b/src/cache/core/cache.ts index 2dffa982b1c..d25c5928332 100644 --- a/src/cache/core/cache.ts +++ b/src/cache/core/cache.ts @@ -1,13 +1,102 @@ import type { DocumentNode } from "graphql"; import { wrap } from "optimism"; -import type { StoreObject, Reference } from "../../utilities/index.js"; -import { getFragmentQueryDocument } from "../../utilities/index.js"; +import type { + StoreObject, + Reference, + DeepPartial, +} from "../../utilities/index.js"; +import { + Observable, + cacheSizes, + defaultCacheSizes, + getFragmentQueryDocument, + mergeDeepArray, +} from "../../utilities/index.js"; import type { DataProxy } from "./types/DataProxy.js"; import type { Cache } from "./types/Cache.js"; +import { WeakCache } from "@wry/caches"; +import { getApolloCacheMemoryInternals } from "../../utilities/caching/getMemoryInternals.js"; +import type { + OperationVariables, + TypedDocumentNode, +} from "../../core/types.js"; +import type { MissingTree } from "./types/common.js"; +import { equalByQuery } from "../../core/equalByQuery.js"; export type Transaction = (c: ApolloCache) => void; +/** + * Watched fragment options. + */ +export interface WatchFragmentOptions { + /** + * A GraphQL fragment document parsed into an AST with the `gql` + * template literal. + * + * @docGroup 1. Required options + */ + fragment: DocumentNode | TypedDocumentNode; + /** + * An object containing a `__typename` and primary key fields + * (such as `id`) identifying the entity object from which the fragment will + * be retrieved, or a `{ __ref: "..." }` reference, or a `string` ID + * (uncommon). + * + * @docGroup 1. Required options + */ + from: StoreObject | Reference | string; + /** + * Any variables that the GraphQL fragment may depend on. + * + * @docGroup 2. Cache options + */ + variables?: TVars; + /** + * The name of the fragment defined in the fragment document. + * + * Required if the fragment document includes more than one fragment, + * optional otherwise. + * + * @docGroup 2. Cache options + */ + fragmentName?: string; + /** + * If `true`, `watchFragment` returns optimistic results. + * + * The default value is `true`. + * + * @docGroup 2. Cache options + */ + optimistic?: boolean; + /** + * @deprecated + * Using `canonizeResults` can result in memory leaks so we generally do not + * recommend using this option anymore. + * A future version of Apollo Client will contain a similar feature. + * + * Whether to canonize cache results before returning them. Canonization + * takes some extra time, but it speeds up future deep equality comparisons. + * Defaults to false. + */ + canonizeResults?: boolean; +} + +/** + * Watched fragment results. + */ +export type WatchFragmentResult = + | { + data: TData; + complete: true; + missing?: never; + } + | { + data: DeepPartial; + complete: false; + missing: MissingTree; + }; + export abstract class ApolloCache implements DataProxy { public readonly assumeImmutableResults: boolean = false; @@ -65,11 +154,9 @@ export abstract class ApolloCache implements DataProxy { // override the batch method to do more interesting things with its options. public batch(options: Cache.BatchOptions): U { const optimisticId = - typeof options.optimistic === "string" - ? options.optimistic - : options.optimistic === false - ? null - : void 0; + typeof options.optimistic === "string" ? options.optimistic + : options.optimistic === false ? null + : void 0; let updateResult: U; this.performTransaction( () => (updateResult = options.update(this)), @@ -126,11 +213,6 @@ export abstract class ApolloCache implements DataProxy { } // DataProxy API - /** - * - * @param options - * @param optimistic - */ public readQuery( options: Cache.ReadQueryOptions, optimistic = !!options.optimistic @@ -142,9 +224,65 @@ export abstract class ApolloCache implements DataProxy { }); } + /** {@inheritDoc @apollo/client!ApolloClient#watchFragment:member(1)} */ + public watchFragment( + options: WatchFragmentOptions + ): Observable> { + const { fragment, fragmentName, from, optimistic = true } = options; + const query = this.getFragmentDoc(fragment, fragmentName); + + const diffOptions: Cache.DiffOptions = { + returnPartialData: true, + id: typeof from === "string" ? from : this.identify(from), + query, + optimistic, + }; + + let latestDiff: DataProxy.DiffResult | undefined; + + return new Observable((observer) => { + return this.watch({ + ...diffOptions, + immediate: true, + callback(diff) { + if ( + // Always ensure we deliver the first result + latestDiff && + equalByQuery( + query, + { data: latestDiff?.result }, + { data: diff.result } + ) + ) { + return; + } + + const result = { + data: diff.result as DeepPartial, + complete: !!diff.complete, + } as WatchFragmentResult; + + if (diff.missing) { + result.missing = mergeDeepArray( + diff.missing.map((error) => error.missing) + ); + } + + latestDiff = diff; + observer.next(result); + }, + }); + }); + } + // Make sure we compute the same (===) fragment query document every // time we receive the same fragment in readFragment. - private getFragmentDoc = wrap(getFragmentQueryDocument); + private getFragmentDoc = wrap(getFragmentQueryDocument, { + max: + cacheSizes["cache.fragmentQueryDocuments"] || + defaultCacheSizes["cache.fragmentQueryDocuments"], + cache: WeakCache, + }); public readFragment( options: Cache.ReadFragmentOptions, @@ -216,4 +354,17 @@ export abstract class ApolloCache implements DataProxy { }, }); } + + /** + * @experimental + * @internal + * This is not a stable API - it is used in development builds to expose + * information to the DevTools. + * Use at your own risk! + */ + public getMemoryInternals?: typeof getApolloCacheMemoryInternals; +} + +if (__DEV__) { + ApolloCache.prototype.getMemoryInternals = getApolloCacheMemoryInternals; } diff --git a/src/cache/core/types/Cache.ts b/src/cache/core/types/Cache.ts index 8628f272a19..0fa70742e15 100644 --- a/src/cache/core/types/Cache.ts +++ b/src/cache/core/types/Cache.ts @@ -14,6 +14,13 @@ export namespace Cache { previousResult?: any; optimistic: boolean; returnPartialData?: boolean; + /** + * @deprecated + * Using `canonizeResults` can result in memory leaks so we generally do not + * recommend using this option anymore. + * A future version of Apollo Client will contain a similar feature without + * the risk of memory leaks. + */ canonizeResults?: boolean; } @@ -93,7 +100,7 @@ export namespace Cache { this: TCache, watch: Cache.WatchOptions, diff: Cache.DiffResult, - lastDiff: Cache.DiffResult | undefined + lastDiff?: Cache.DiffResult | undefined ) => any; } diff --git a/src/cache/core/types/DataProxy.ts b/src/cache/core/types/DataProxy.ts index 6dbdf47b75d..4e3e0f9cc73 100644 --- a/src/cache/core/types/DataProxy.ts +++ b/src/cache/core/types/DataProxy.ts @@ -68,11 +68,7 @@ export namespace DataProxy { * readQuery method can be omitted. Defaults to false. */ optimistic?: boolean; - /** - * Whether to canonize cache results before returning them. Canonization - * takes some extra time, but it speeds up future deep equality comparisons. - * Defaults to false. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ canonizeResults?: boolean; } @@ -89,11 +85,7 @@ export namespace DataProxy { * readQuery method can be omitted. Defaults to false. */ optimistic?: boolean; - /** - * Whether to canonize cache results before returning them. Canonization - * takes some extra time, but it speeds up future deep equality comparisons. - * Defaults to false. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ canonizeResults?: boolean; } diff --git a/src/cache/core/types/common.ts b/src/cache/core/types/common.ts index 19200d4845e..886ccb63458 100644 --- a/src/cache/core/types/common.ts +++ b/src/cache/core/types/common.ts @@ -87,6 +87,10 @@ declare const _invalidateModifier: unique symbol; export interface InvalidateModifier { [_invalidateModifier]: true; } +declare const _ignoreModifier: unique symbol; +export interface IgnoreModifier { + [_ignoreModifier]: true; +} export type ModifierDetails = { DELETE: DeleteModifier; @@ -105,18 +109,20 @@ export type Modifier = ( details: ModifierDetails ) => T | DeleteModifier | InvalidateModifier; -type StoreObjectValueMaybeReference = StoreVal extends Array< - infer Item extends Record -> - ? ReadonlyArray | Reference> - : StoreVal extends Record - ? AsStoreObject | Reference +type StoreObjectValueMaybeReference = + StoreVal extends Array> ? + StoreVal extends Array ? + Item extends Record ? + ReadonlyArray | Reference> + : never + : never + : StoreVal extends Record ? AsStoreObject | Reference : StoreVal; export type AllFieldsModifier> = Modifier< - Entity[keyof Entity] extends infer Value - ? StoreObjectValueMaybeReference> - : never + Entity[keyof Entity] extends infer Value ? + StoreObjectValueMaybeReference> + : never >; export type Modifiers = Record> = diff --git a/src/cache/index.ts b/src/cache/index.ts index bed4ad849fd..1f55d8c16df 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -1,6 +1,10 @@ import "../utilities/globals/index.js"; -export type { Transaction } from "./core/cache.js"; +export type { + Transaction, + WatchFragmentOptions, + WatchFragmentResult, +} from "./core/cache.js"; export { ApolloCache } from "./core/cache.js"; export { Cache } from "./core/types/Cache.js"; export type { DataProxy } from "./core/types/DataProxy.js"; @@ -14,7 +18,11 @@ export type { export { MissingFieldError } from "./core/types/common.js"; export type { Reference } from "../utilities/index.js"; -export { isReference, makeReference } from "../utilities/index.js"; +export { + isReference, + makeReference, + canonicalStringify, +} from "../utilities/index.js"; export { EntityStore } from "./inmemory/entityStore.js"; export { @@ -38,8 +46,6 @@ export type { } from "./inmemory/policies.js"; export { Policies } from "./inmemory/policies.js"; -export { canonicalStringify } from "./inmemory/object-canon.js"; - export type { FragmentRegistryAPI } from "./inmemory/fragmentRegistry.js"; export { createFragmentRegistry } from "./inmemory/fragmentRegistry.js"; diff --git a/src/cache/inmemory/__tests__/__snapshots__/writeToStore.ts.snap b/src/cache/inmemory/__tests__/__snapshots__/writeToStore.ts.snap index c5dcfb3571a..24b9ab6638c 100644 --- a/src/cache/inmemory/__tests__/__snapshots__/writeToStore.ts.snap +++ b/src/cache/inmemory/__tests__/__snapshots__/writeToStore.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`writing to the store "Cache data maybe lost..." warnings should not warn when scalar fields are updated 1`] = ` +exports[`writing to the store "Cache data may be lost..." warnings should not warn when scalar fields are updated 1`] = ` Object { "ROOT_QUERY": Object { "__typename": "Query", @@ -18,7 +18,7 @@ Object { } `; -exports[`writing to the store "Cache data maybe lost..." warnings should not warn when scalar fields are updated 2`] = ` +exports[`writing to the store "Cache data may be lost..." warnings should not warn when scalar fields are updated 2`] = ` Object { "ROOT_QUERY": Object { "__typename": "Query", @@ -34,7 +34,68 @@ Object { } `; -exports[`writing to the store "Cache data maybe lost..." warnings should not warn when scalar fields are updated 3`] = `[MockFunction]`; +exports[`writing to the store "Cache data may be lost..." warnings should not warn when scalar fields are updated 3`] = `[MockFunction]`; + +exports[`writing to the store "Cache data may be lost..." warnings should warn "Cache data may be lost..." message 1`] = ` +Object { + "ROOT_QUERY": Object { + "__typename": "Query", + "someJSON": Object { + "name": "Tom", + }, + }, +} +`; + +exports[`writing to the store "Cache data may be lost..." warnings should warn "Cache data may be lost..." message 2`] = ` +Object { + "ROOT_QUERY": Object { + "__typename": "Query", + "someJSON": Object { + "age": 20, + }, + }, +} +`; + +exports[`writing to the store "Cache data may be lost..." warnings should warn "Cache data may be lost..." message 3`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "Cache data may be lost when replacing the %s field of a %s object. + +This could cause additional (usually avoidable) network requests to fetch data that were otherwise cached. + +To address this problem (which is not a bug in Apollo Client), %sdefine a custom merge function for the %s field, so InMemoryCache can safely merge these objects: + + existing: %o + incoming: %o + +For more information about these options, please refer to the documentation: + + * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers + * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects +", + "someJSON", + "Query", + "", + "Query.someJSON", + Object { + "name": "Tom", + }, + Object { + "age": 20, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; exports[`writing to the store correctly merges fragment fields along multiple paths 1`] = ` Object { diff --git a/src/cache/inmemory/__tests__/cache.ts b/src/cache/inmemory/__tests__/cache.ts index a7289cd2c64..2d426ed7207 100644 --- a/src/cache/inmemory/__tests__/cache.ts +++ b/src/cache/inmemory/__tests__/cache.ts @@ -19,6 +19,7 @@ import { StoreWriter } from "../writeToStore"; import { ObjectCanon } from "../object-canon"; import { TypePolicies } from "../policies"; import { spyOnConsole } from "../../../testing/internal"; +import { defaultCacheSizes } from "../../../utilities"; disableFragmentWarnings(); @@ -2119,15 +2120,17 @@ describe("Cache", () => { }); describe("resultCacheMaxSize", () => { - const defaultMaxSize = Math.pow(2, 16); - it("uses default max size on caches if resultCacheMaxSize is not configured", () => { const cache = new InMemoryCache(); - expect(cache["maybeBroadcastWatch"].options.max).toBe(defaultMaxSize); + expect(cache["maybeBroadcastWatch"].options.max).toBe( + defaultCacheSizes["inMemoryCache.maybeBroadcastWatch"] + ); expect(cache["storeReader"]["executeSelectionSet"].options.max).toBe( - defaultMaxSize + defaultCacheSizes["inMemoryCache.executeSelectionSet"] + ); + expect(cache["getFragmentDoc"].options.max).toBe( + defaultCacheSizes["cache.fragmentQueryDocuments"] ); - expect(cache["getFragmentDoc"].options.max).toBe(defaultMaxSize); }); it("configures max size on caches when resultCacheMaxSize is set", () => { @@ -2137,7 +2140,9 @@ describe("resultCacheMaxSize", () => { expect(cache["storeReader"]["executeSelectionSet"].options.max).toBe( resultCacheMaxSize ); - expect(cache["getFragmentDoc"].options.max).toBe(defaultMaxSize); + expect(cache["getFragmentDoc"].options.max).toBe( + defaultCacheSizes["cache.fragmentQueryDocuments"] + ); }); }); @@ -4035,6 +4040,7 @@ describe("ReactiveVar and makeVar", () => { let broadcastCount = 0; cache["broadcastWatches"] = function () { ++broadcastCount; + // @ts-expect-error return broadcast.apply(this, arguments); }; diff --git a/src/cache/inmemory/__tests__/diffAgainstStore.ts b/src/cache/inmemory/__tests__/diffAgainstStore.ts index 5beef906247..0a315c60270 100644 --- a/src/cache/inmemory/__tests__/diffAgainstStore.ts +++ b/src/cache/inmemory/__tests__/diffAgainstStore.ts @@ -493,8 +493,8 @@ describe("diffing queries against the store", () => { }; const cache = new InMemoryCache({ - dataIdFromObject({ id }: { id: string }) { - return id; + dataIdFromObject(obj: any) { + return obj.id; }, }); @@ -841,7 +841,7 @@ describe("diffing queries against the store", () => { const writer = new StoreWriter( new InMemoryCache({ - dataIdFromObject: ({ id }: { id: string }) => id, + dataIdFromObject: (obj: any) => obj.id, }) ); @@ -1067,7 +1067,7 @@ describe("diffing queries against the store", () => { }); throw new Error("should have thrown"); } catch (e) { - expect(e.message).toEqual( + expect((e as Error).message).toEqual( "Missing selection set for object of type Message returned for query field messageList" ); } diff --git a/src/cache/inmemory/__tests__/key-extractor.ts b/src/cache/inmemory/__tests__/key-extractor.ts index 3636f6490e6..d525263d010 100644 --- a/src/cache/inmemory/__tests__/key-extractor.ts +++ b/src/cache/inmemory/__tests__/key-extractor.ts @@ -1,5 +1,5 @@ import { KeySpecifier } from "../policies"; -import { canonicalStringify } from "../object-canon"; +import { canonicalStringify } from "../../../utilities"; import { getSpecifierPaths, collectSpecifierPaths, diff --git a/src/cache/inmemory/__tests__/readFromStore.ts b/src/cache/inmemory/__tests__/readFromStore.ts index 99472adc335..d936ad73734 100644 --- a/src/cache/inmemory/__tests__/readFromStore.ts +++ b/src/cache/inmemory/__tests__/readFromStore.ts @@ -17,14 +17,16 @@ import { isReference, TypedDocumentNode, } from "../../../core"; +import { defaultCacheSizes } from "../../../utilities"; describe("resultCacheMaxSize", () => { const cache = new InMemoryCache(); - const defaultMaxSize = Math.pow(2, 16); it("uses default max size on caches if resultCacheMaxSize is not configured", () => { const reader = new StoreReader({ cache }); - expect(reader["executeSelectionSet"].options.max).toBe(defaultMaxSize); + expect(reader["executeSelectionSet"].options.max).toBe( + defaultCacheSizes["inMemoryCache.executeSelectionSet"] + ); }); it("configures max size on caches when resultCacheMaxSize is set", () => { @@ -558,6 +560,69 @@ describe("reading from the store", () => { }); }); + it("runs a nested query - skips iterating into an empty array", () => { + const reader = new StoreReader({ + cache: new InMemoryCache(), + }); + + const result = { + id: "abcd", + stringField: "This is a string!", + numberField: 5, + nullField: null, + nestedArray: [ + { + id: "abcde", + stringField: "This is a string also!", + numberField: 7, + nullField: null, + }, + ], + emptyArray: [], + } satisfies StoreObject; + + const store = defaultNormalizedCacheFactory({ + ROOT_QUERY: { ...result, nestedArray: [makeReference("abcde")] }, + abcde: result.nestedArray[0], + }); + + expect(reader["executeSubSelectedArray"].size).toBe(0); + + // assumption: cache size does not increase for empty array + readQueryFromStore(reader, { + store, + query: gql` + { + stringField + numberField + emptyArray { + id + stringField + numberField + } + } + `, + }); + expect(reader["executeSubSelectedArray"].size).toBe(0); + + // assumption: cache size increases for array with content + readQueryFromStore(reader, { + store, + query: gql` + { + stringField + numberField + nestedArray { + id + stringField + numberField + } + } + `, + }); + expect(reader["executeSubSelectedArray"].size).toBe(1); + }); + it("throws on a missing field", () => { const result = { id: "abcd", @@ -1477,12 +1542,12 @@ describe("reading from the store", () => { fields: { ruler(ruler, { canRead, toReference }) { // If the throne is empty, promote Apollo! - return canRead(ruler) - ? ruler - : toReference({ + return canRead(ruler) ? ruler : ( + toReference({ __typename: "Deity", name: "Apollo", - }); + }) + ); }, }, }, @@ -1654,8 +1719,8 @@ describe("reading from the store", () => { ...diffWithoutDevouredSons.result.ruler, children: diffWithoutDevouredSons.result.ruler.children.map( (child) => { - return child.name === "Zeus" - ? { + return child.name === "Zeus" ? + { ...child, children: childrenOfZeus // Remove empty child.children arrays. @@ -1904,7 +1969,7 @@ describe("reading from the store", () => { ); expect(value.__ref).toBe('Deity:{"name":"Zeus"}'); // Interim ruler Apollo takes over for real. - return toReference(apolloRulerResult.ruler); + return toReference(apolloRulerResult.ruler)!; }, }, }); diff --git a/src/cache/inmemory/__tests__/roundtrip.ts b/src/cache/inmemory/__tests__/roundtrip.ts index 6f04fabe5d7..5c6824cdc6e 100644 --- a/src/cache/inmemory/__tests__/roundtrip.ts +++ b/src/cache/inmemory/__tests__/roundtrip.ts @@ -61,7 +61,7 @@ function storeRoundtrip(query: DocumentNode, result: any, variables = {}) { (immutableResult as any).illegal = "this should not work"; throw new Error("unreached"); } catch (e) { - expect(e.message).not.toMatch(/unreached/); + expect((e as Error).message).not.toMatch(/unreached/); expect(e).toBeInstanceOf(TypeError); } assertDeeplyFrozen(immutableResult); diff --git a/src/cache/inmemory/__tests__/writeToStore.ts b/src/cache/inmemory/__tests__/writeToStore.ts index f1911b0a3c4..8c8bf1c5b48 100644 --- a/src/cache/inmemory/__tests__/writeToStore.ts +++ b/src/cache/inmemory/__tests__/writeToStore.ts @@ -25,9 +25,14 @@ import { defaultNormalizedCacheFactory, writeQueryToStore } from "./helpers"; import { InMemoryCache } from "../inMemoryCache"; import { TypedDocumentNode } from "../../../core"; import { extractFragmentContext } from "../helpers"; +import { KeyFieldsFunction } from "../policies"; +import { invariant } from "../../../utilities/globals"; import { spyOnConsole } from "../../../testing/internal"; -const getIdField = ({ id }: { id: string }) => id; +const getIdField: KeyFieldsFunction = ({ id }) => { + invariant(typeof id === "string", "id is not a string"); + return id; +}; describe("writing to the store", () => { const cache = new InMemoryCache({ @@ -1293,19 +1298,17 @@ describe("writing to the store", () => { } testData.forEach((data) => { - data.mutation.definitions.forEach( - (definition: OperationDefinitionNode) => { - if (isOperationDefinition(definition)) { - definition.selectionSet.selections.forEach((selection) => { - if (isField(selection)) { - expect( - storeKeyNameFromField(selection, data.variables) - ).toEqual(data.expected); - } - }); - } + data.mutation.definitions.forEach((definition) => { + if (isOperationDefinition(definition)) { + definition.selectionSet.selections.forEach((selection) => { + if (isField(selection)) { + expect(storeKeyNameFromField(selection, data.variables)).toEqual( + data.expected + ); + } + }); } - ); + }); }); }); @@ -1357,7 +1360,7 @@ describe("writing to the store", () => { return value.kind === "OperationDefinition"; } - mutation.definitions.map((def: OperationDefinitionNode) => { + mutation.definitions.map((def) => { if (isOperationDefinition(def)) { const writer = new StoreWriter( new InMemoryCache({ @@ -2044,7 +2047,43 @@ describe("writing to the store", () => { }); }); - describe('"Cache data maybe lost..." warnings', () => { + describe('"Cache data may be lost..." warnings', () => { + it('should warn "Cache data may be lost..." message', () => { + using _consoleSpy = spyOnConsole.takeSnapshots("warn"); + const cache = new InMemoryCache(); + + const query = gql` + query { + someJSON { + name + age + } + } + `; + + cache.writeQuery({ + query, + data: { + someJSON: { + name: "Tom", + }, + }, + }); + + expect(cache.extract()).toMatchSnapshot(); + + cache.writeQuery({ + query, + data: { + someJSON: { + age: 20, + }, + }, + }); + + expect(cache.extract()).toMatchSnapshot(); + }); + it("should not warn when scalar fields are updated", () => { using _consoleSpy = spyOnConsole.takeSnapshots("warn"); const cache = new InMemoryCache(); diff --git a/src/cache/inmemory/entityStore.ts b/src/cache/inmemory/entityStore.ts index ddb03b274da..c0582843fdc 100644 --- a/src/cache/inmemory/entityStore.ts +++ b/src/cache/inmemory/entityStore.ts @@ -32,6 +32,7 @@ import type { DeleteModifier, ModifierDetails, } from "../core/types/common.js"; +import type { DocumentNode, FieldNode, SelectionSetNode } from "graphql"; const DELETE: DeleteModifier = Object.create(null); const delModifier: Modifier = () => DELETE; @@ -221,12 +222,12 @@ export abstract class EntityStore implements NormalizedCache { from?: StoreObject | Reference ) => this.policies.readField( - typeof fieldNameOrOptions === "string" - ? { - fieldName: fieldNameOrOptions, - from: from || makeReference(dataId), - } - : fieldNameOrOptions, + typeof fieldNameOrOptions === "string" ? + { + fieldName: fieldNameOrOptions, + from: from || makeReference(dataId), + } + : fieldNameOrOptions, { store: this } ), } satisfies Partial; @@ -236,19 +237,19 @@ export abstract class EntityStore implements NormalizedCache { let fieldValue = storeObject[storeFieldName]; if (fieldValue === void 0) return; const modify: Modifier | undefined = - typeof fields === "function" - ? fields - : fields[storeFieldName] || fields[fieldName]; + typeof fields === "function" ? fields : ( + fields[storeFieldName] || fields[fieldName] + ); if (modify) { let newValue = - modify === delModifier - ? DELETE - : modify(maybeDeepFreeze(fieldValue), { - ...sharedDetails, - fieldName, - storeFieldName, - storage: this.getStorage(dataId, storeFieldName), - }); + modify === delModifier ? DELETE : ( + modify(maybeDeepFreeze(fieldValue), { + ...sharedDetails, + fieldName, + storeFieldName, + storage: this.getStorage(dataId, storeFieldName), + }) + ); if (newValue === INVALIDATE) { this.group.dirty(dataId, storeFieldName); } else { @@ -344,16 +345,16 @@ export abstract class EntityStore implements NormalizedCache { if (storeObject) { const typename = this.getFieldValue(storeObject, "__typename"); const storeFieldName = - fieldName && args - ? this.policies.getStoreFieldName({ typename, fieldName, args }) - : fieldName; + fieldName && args ? + this.policies.getStoreFieldName({ typename, fieldName, args }) + : fieldName; return this.modify( dataId, - storeFieldName - ? { - [storeFieldName]: delModifier, - } - : delModifier + storeFieldName ? + { + [storeFieldName]: delModifier, + } + : delModifier ); } return false; @@ -522,6 +523,27 @@ export abstract class EntityStore implements NormalizedCache { } // Used to compute cache keys specific to this.group. + /** overload for `InMemoryCache.maybeBroadcastWatch` */ + public makeCacheKey( + document: DocumentNode, + callback: Cache.WatchCallback, + details: string + ): object; + /** overload for `StoreReader.executeSelectionSet` */ + public makeCacheKey( + selectionSet: SelectionSetNode, + parent: string /* = ( Reference.__ref ) */ | StoreObject, + varString: string | undefined, + canonizeResults: boolean + ): object; + /** overload for `StoreReader.executeSubSelectedArray` */ + public makeCacheKey( + field: FieldNode, + array: readonly any[], + varString: string | undefined + ): object; + /** @deprecated This is only meant for internal usage, + * in your own code please use a `Trie` instance instead. */ public makeCacheKey(...args: any[]): object; public makeCacheKey() { return this.group.keyMaker.lookupArray(arguments); @@ -534,17 +556,17 @@ export abstract class EntityStore implements NormalizedCache { storeFieldName: string ) => maybeDeepFreeze( - isReference(objectOrReference) - ? this.get(objectOrReference.__ref, storeFieldName) - : objectOrReference && objectOrReference[storeFieldName] + isReference(objectOrReference) ? + this.get(objectOrReference.__ref, storeFieldName) + : objectOrReference && objectOrReference[storeFieldName] ) as SafeReadonly; // Returns true for non-normalized StoreObjects and non-dangling // References, indicating that readField(name, objOrRef) has a chance of // working. Useful for filtering out dangling references from lists. public canRead: CanReadFunction = (objOrRef) => { - return isReference(objOrRef) - ? this.has(objOrRef.__ref) + return isReference(objOrRef) ? + this.has(objOrRef.__ref) : typeof objOrRef === "object"; }; @@ -593,7 +615,7 @@ class CacheGroup { // Used by the EntityStore#makeCacheKey method to compute cache keys // specific to this CacheGroup. - public keyMaker: Trie; + public keyMaker!: Trie; constructor( public readonly caching: boolean, @@ -789,8 +811,8 @@ class Layer extends EntityStore { public findChildRefIds(dataId: string): Record { const fromParent = this.parent.findChildRefIds(dataId); - return hasOwn.call(this.data, dataId) - ? { + return hasOwn.call(this.data, dataId) ? + { ...fromParent, ...super.findChildRefIds(dataId), } @@ -800,7 +822,11 @@ class Layer extends EntityStore { public getStorage(): StorageType { let p: EntityStore = this.parent; while ((p as Layer).parent) p = (p as Layer).parent; - return p.getStorage.apply(p, arguments); + return p.getStorage.apply( + p, + // @ts-expect-error + arguments + ); } } @@ -823,20 +849,20 @@ class Stump extends Layer { return this; } - public merge() { + public merge(older: string | StoreObject, newer: string | StoreObject) { // We never want to write any data into the Stump, so we forward any merge // calls to the Root instead. Another option here would be to throw an // exception, but the toReference(object, true) function can sometimes // trigger Stump writes (which used to be Root writes, before the Stump // concept was introduced). - return this.parent.merge.apply(this.parent, arguments); + return this.parent.merge(older, newer); } } function storeObjectReconciler( existingObject: StoreObject, incomingObject: StoreObject, - property: string + property: string | number ): StoreValue { const existingValue = existingObject[property]; const incomingValue = incomingObject[property]; diff --git a/src/cache/inmemory/fixPolyfills.native.ts b/src/cache/inmemory/fixPolyfills.native.ts index d2302429c74..4b75824841a 100644 --- a/src/cache/inmemory/fixPolyfills.native.ts +++ b/src/cache/inmemory/fixPolyfills.native.ts @@ -33,10 +33,10 @@ try { // https://github.com/apollographql/react-apollo/issues/2442#issuecomment-426489517 testMap.set(frozen, frozen).delete(frozen); } catch { - const wrap = (method: (obj: T) => T): typeof method => { + const wrap = (obj: T) => T>(method: M): M => { return ( method && - ((obj) => { + (((obj) => { try { // If .set succeeds, also call .delete to avoid leaking memory. testMap.set(obj, obj).delete(obj); @@ -45,7 +45,7 @@ try { // by this return-from-finally statement: return method.call(Object, obj); } - }) + }) as M) ); }; Object.freeze = wrap(Object.freeze); diff --git a/src/cache/inmemory/fragmentRegistry.ts b/src/cache/inmemory/fragmentRegistry.ts index e868122d1d2..28463594c9d 100644 --- a/src/cache/inmemory/fragmentRegistry.ts +++ b/src/cache/inmemory/fragmentRegistry.ts @@ -9,12 +9,18 @@ import { visit } from "graphql"; import { wrap } from "optimism"; import type { FragmentMap } from "../../utilities/index.js"; -import { getFragmentDefinitions } from "../../utilities/index.js"; +import { + cacheSizes, + defaultCacheSizes, + getFragmentDefinitions, +} from "../../utilities/index.js"; +import { WeakCache } from "@wry/caches"; export interface FragmentRegistryAPI { register(...fragments: DocumentNode[]): this; lookup(fragmentName: string): FragmentDefinitionNode | null; transform(document: D): D; + resetCaches(): void; } // As long as createFragmentRegistry is not imported or used, the @@ -29,24 +35,22 @@ export function createFragmentRegistry( return new FragmentRegistry(...fragments); } -const { forEach: arrayLikeForEach } = Array.prototype; - class FragmentRegistry implements FragmentRegistryAPI { private registry: FragmentMap = Object.create(null); - // Call static method FragmentRegistry.from(...) instead of invoking the + // Call `createFragmentRegistry` instead of invoking the // FragmentRegistry constructor directly. This reserves the constructor for // future configuration of the FragmentRegistry. constructor(...fragments: DocumentNode[]) { this.resetCaches(); if (fragments.length) { - this.register.apply(this, fragments); + this.register(...fragments); } } - public register(): this { + public register(...fragments: DocumentNode[]): this { const definitions = new Map(); - arrayLikeForEach.call(arguments, (doc: DocumentNode) => { + fragments.forEach((doc: DocumentNode) => { getFragmentDefinitions(doc).forEach((node) => { definitions.set(node.name.value, node); }); @@ -66,29 +70,32 @@ class FragmentRegistry implements FragmentRegistryAPI { private invalidate(name: string) {} public resetCaches() { - this.invalidate = (this.lookup = this.cacheUnaryMethod("lookup")).dirty; // This dirty function is bound to the wrapped lookup method. - this.transform = this.cacheUnaryMethod("transform"); - this.findFragmentSpreads = this.cacheUnaryMethod("findFragmentSpreads"); - } - - private cacheUnaryMethod< - TName extends keyof Pick< - FragmentRegistry, - "lookup" | "transform" | "findFragmentSpreads" - >, - >(name: TName) { - const registry = this; - const originalMethod = FragmentRegistry.prototype[name]; - return wrap( - function () { - return originalMethod.apply(registry, arguments); - }, - { - makeCacheKey: (arg) => arg, - } - ); + const proto = FragmentRegistry.prototype; + this.invalidate = (this.lookup = wrap(proto.lookup.bind(this), { + makeCacheKey: (arg) => arg, + max: + cacheSizes["fragmentRegistry.lookup"] || + defaultCacheSizes["fragmentRegistry.lookup"], + })).dirty; // This dirty function is bound to the wrapped lookup method. + this.transform = wrap(proto.transform.bind(this), { + cache: WeakCache, + max: + cacheSizes["fragmentRegistry.transform"] || + defaultCacheSizes["fragmentRegistry.transform"], + }); + this.findFragmentSpreads = wrap(proto.findFragmentSpreads.bind(this), { + cache: WeakCache, + max: + cacheSizes["fragmentRegistry.findFragmentSpreads"] || + defaultCacheSizes["fragmentRegistry.findFragmentSpreads"], + }); } + /* + * Note: + * This method is only memoized so it can serve as a dependency to `tranform`, + * so calling `invalidate` will invalidate cache entries for `transform`. + */ public lookup(fragmentName: string): FragmentDefinitionNode | null { return this.registry[fragmentName] || null; } diff --git a/src/cache/inmemory/helpers.ts b/src/cache/inmemory/helpers.ts index f18d34da3f2..f8991653aed 100644 --- a/src/cache/inmemory/helpers.ts +++ b/src/cache/inmemory/helpers.ts @@ -43,10 +43,9 @@ export function defaultDataIdFromObject( ): string | undefined { if (typeof __typename === "string") { if (context) { - context.keyObject = !isNullish(id) - ? { id } - : !isNullish(_id) - ? { _id } + context.keyObject = + !isNullish(id) ? { id } + : !isNullish(_id) ? { _id } : void 0; } @@ -57,9 +56,9 @@ export function defaultDataIdFromObject( if (!isNullish(id)) { return `${__typename}:${ - typeof id === "number" || typeof id === "string" - ? id - : JSON.stringify(id) + typeof id === "number" || typeof id === "string" ? + id + : JSON.stringify(id) }`; } } @@ -89,8 +88,8 @@ export function getTypenameFromStoreObject( store: NormalizedCache, objectOrReference: StoreObject | Reference ): string | undefined { - return isReference(objectOrReference) - ? (store.get(objectOrReference.__ref, "__typename") as string) + return isReference(objectOrReference) ? + (store.get(objectOrReference.__ref, "__typename") as string) : objectOrReference && objectOrReference.__typename; } @@ -107,8 +106,8 @@ export function selectionSetMatchesResult( variables?: Record ): boolean { if (isNonNullObject(result)) { - return isArray(result) - ? result.every((item) => + return isArray(result) ? + result.every((item) => selectionSetMatchesResult(selectionSet, item, variables) ) : selectionSet.selections.every((field) => { diff --git a/src/cache/inmemory/inMemoryCache.ts b/src/cache/inmemory/inMemoryCache.ts index 2dac460bf54..fe62023f165 100644 --- a/src/cache/inmemory/inMemoryCache.ts +++ b/src/cache/inmemory/inMemoryCache.ts @@ -16,6 +16,10 @@ import { addTypenameToDocument, isReference, DocumentTransform, + canonicalStringify, + print, + cacheSizes, + defaultCacheSizes, } from "../../utilities/index.js"; import type { InMemoryCacheConfig, NormalizedCacheObject } from "./types.js"; import { StoreReader } from "./readFromStore.js"; @@ -24,8 +28,8 @@ import { EntityStore, supportsResultCaching } from "./entityStore.js"; import { makeVar, forgetCache, recallCache } from "./reactiveVars.js"; import { Policies } from "./policies.js"; import { hasOwn, normalizeConfig, shouldCanonizeResults } from "./helpers.js"; -import { canonicalStringify } from "./object-canon.js"; import type { OperationVariables } from "../../core/index.js"; +import { getInMemoryCacheMemoryInternals } from "../../utilities/caching/getMemoryInternals.js"; type BroadcastOptions = Pick< Cache.BatchOptions, @@ -33,18 +37,18 @@ type BroadcastOptions = Pick< >; export class InMemoryCache extends ApolloCache { - private data: EntityStore; - private optimisticData: EntityStore; + private data!: EntityStore; + private optimisticData!: EntityStore; protected config: InMemoryCacheConfig; private watches = new Set(); private addTypename: boolean; - private storeReader: StoreReader; - private storeWriter: StoreWriter; + private storeReader!: StoreReader; + private storeWriter!: StoreWriter; private addTypenameTransform = new DocumentTransform(addTypenameToDocument); - private maybeBroadcastWatch: OptimisticWrapperFunction< + private maybeBroadcastWatch!: OptimisticWrapperFunction< [Cache.WatchOptions, BroadcastOptions?], any, [Cache.WatchOptions] @@ -109,9 +113,10 @@ export class InMemoryCache extends ApolloCache { addTypename: this.addTypename, resultCacheMaxSize: this.config.resultCacheMaxSize, canonizeResults: shouldCanonizeResults(this.config), - canon: resetResultIdentities - ? void 0 - : previousReader && previousReader.canon, + canon: + resetResultIdentities ? void 0 : ( + previousReader && previousReader.canon + ), fragments, })), fragments @@ -122,7 +127,10 @@ export class InMemoryCache extends ApolloCache { return this.broadcastWatch(c, options); }, { - max: this.config.resultCacheMaxSize, + max: + this.config.resultCacheMaxSize || + cacheSizes["inMemoryCache.maybeBroadcastWatch"] || + defaultCacheSizes["inMemoryCache.maybeBroadcastWatch"], makeCacheKey: (c: Cache.WatchOptions) => { // Return a cache key (thus enabling result caching) only if we're // currently using a data store that can track cache dependencies. @@ -225,8 +233,11 @@ export class InMemoryCache extends ApolloCache { // that nothing was modified. return false; } - const store = options.optimistic // Defaults to false. - ? this.optimisticData + const store = + ( + options.optimistic // Defaults to false. + ) ? + this.optimisticData : this.data; try { ++this.txCount; @@ -293,6 +304,9 @@ export class InMemoryCache extends ApolloCache { resetResultIdentities?: boolean; }) { canonicalStringify.reset(); + print.reset(); + this.addTypenameTransform.resetCache(); + this.config.fragments?.resetCaches(); const ids = this.optimisticData.gc(); if (options && !this.txCount) { if (options.resetResultCache) { @@ -568,4 +582,17 @@ export class InMemoryCache extends ApolloCache { c.callback((c.lastDiff = diff), lastDiff); } } + + /** + * @experimental + * @internal + * This is not a stable API - it is used in development builds to expose + * information to the DevTools. + * Use at your own risk! + */ + public getMemoryInternals?: typeof getInMemoryCacheMemoryInternals; +} + +if (__DEV__) { + InMemoryCache.prototype.getMemoryInternals = getInMemoryCacheMemoryInternals; } diff --git a/src/cache/inmemory/key-extractor.ts b/src/cache/inmemory/key-extractor.ts index 314da216800..38c5d14698e 100644 --- a/src/cache/inmemory/key-extractor.ts +++ b/src/cache/inmemory/key-extractor.ts @@ -250,8 +250,8 @@ export function extractKeyPath( extract = extract || extractKey; return normalize( path.reduce(function reducer(obj, key): any { - return isArray(obj) - ? obj.map((child) => reducer(child, key)) + return isArray(obj) ? + obj.map((child) => reducer(child, key)) : obj && extract!(obj, key); }, object) ); diff --git a/src/cache/inmemory/object-canon.ts b/src/cache/inmemory/object-canon.ts index 376b474f282..e69a8b1bbeb 100644 --- a/src/cache/inmemory/object-canon.ts +++ b/src/cache/inmemory/object-canon.ts @@ -8,8 +8,8 @@ import { isArray } from "./helpers.js"; function shallowCopy(value: T): T { if (isObjectOrArray(value)) { - return isArray(value) - ? (value.slice(0) as any as T) + return isArray(value) ? + (value.slice(0) as any as T) : { __proto__: Object.getPrototypeOf(value), ...value }; } return value; @@ -195,35 +195,3 @@ type SortedKeysInfo = { sorted: string[]; json: string; }; - -// Since the keys of canonical objects are always created in lexicographically -// sorted order, we can use the ObjectCanon to implement a fast and stable -// version of JSON.stringify, which automatically sorts object keys. -export const canonicalStringify = Object.assign( - function (value: any): string { - if (isObjectOrArray(value)) { - if (stringifyCanon === void 0) { - resetCanonicalStringify(); - } - const canonical = stringifyCanon.admit(value); - let json = stringifyCache.get(canonical); - if (json === void 0) { - stringifyCache.set(canonical, (json = JSON.stringify(canonical))); - } - return json; - } - return JSON.stringify(value); - }, - { - reset: resetCanonicalStringify, - } -); - -// Can be reset by calling canonicalStringify.reset(). -let stringifyCanon: ObjectCanon; -let stringifyCache: WeakMap; - -function resetCanonicalStringify() { - stringifyCanon = new ObjectCanon(); - stringifyCache = new (canUseWeakMap ? WeakMap : Map)(); -} diff --git a/src/cache/inmemory/policies.ts b/src/cache/inmemory/policies.ts index 2b2caec7e9c..3c68f35eb9d 100644 --- a/src/cache/inmemory/policies.ts +++ b/src/cache/inmemory/policies.ts @@ -48,17 +48,11 @@ import type { } from "../core/types/common.js"; import type { WriteContext } from "./writeToStore.js"; -// Upgrade to a faster version of the default stable JSON.stringify function -// used by getStoreKeyName. This function is used when computing storeFieldName -// strings (when no keyArgs has been configured for a field). -import { canonicalStringify } from "./object-canon.js"; import { keyArgsFnFromSpecifier, keyFieldsFnFromSpecifier, } from "./key-extractor.js"; -getStoreKeyName.setStringify(canonicalStringify); - export type TypePolicies = { [__typename: string]: TypePolicy; }; @@ -172,11 +166,11 @@ export type FieldPolicy< export type StorageType = Record; function argsFromFieldSpecifier(spec: FieldSpecifier) { - return spec.args !== void 0 - ? spec.args - : spec.field - ? argumentsObjectFromField(spec.field, spec.variables) - : null; + return ( + spec.args !== void 0 ? spec.args + : spec.field ? argumentsObjectFromField(spec.field, spec.variables) + : null + ); } export interface FieldFunctionOptions< @@ -451,17 +445,14 @@ export class Policies { merge?: FieldMergeFunction | boolean ) { existing.merge = - typeof merge === "function" - ? merge - : // Pass merge:true as a shorthand for a merge implementation + typeof merge === "function" ? merge + // Pass merge:true as a shorthand for a merge implementation // that returns options.mergeObjects(existing, incoming). - merge === true - ? mergeTrueFn - : // Pass merge:false to make incoming always replace existing + : merge === true ? mergeTrueFn + // Pass merge:false to make incoming always replace existing // without any warnings about data clobbering. - merge === false - ? mergeFalseFn - : existing.merge; + : merge === false ? mergeFalseFn + : existing.merge; } // Type policies can define merge functions, as an alternative to @@ -470,17 +461,14 @@ export class Policies { existing.keyFn = // Pass false to disable normalization for this typename. - keyFields === false - ? nullKeyFieldsFn - : // Pass an array of strings to use those fields to compute a + keyFields === false ? nullKeyFieldsFn + // Pass an array of strings to use those fields to compute a // composite ID for objects of this typename. - isArray(keyFields) - ? keyFieldsFnFromSpecifier(keyFields) - : // Pass a function to take full control over identification. - typeof keyFields === "function" - ? keyFields - : // Leave existing.keyFn unchanged if above cases fail. - existing.keyFn; + : isArray(keyFields) ? keyFieldsFnFromSpecifier(keyFields) + // Pass a function to take full control over identification. + : typeof keyFields === "function" ? keyFields + // Leave existing.keyFn unchanged if above cases fail. + : existing.keyFn; if (fields) { Object.keys(fields).forEach((fieldName) => { @@ -495,17 +483,14 @@ export class Policies { existing.keyFn = // Pass false to disable argument-based differentiation of // field identities. - keyArgs === false - ? simpleKeyArgsFn - : // Pass an array of strings to use named arguments to + keyArgs === false ? simpleKeyArgsFn + // Pass an array of strings to use named arguments to // compute a composite identity for the field. - isArray(keyArgs) - ? keyArgsFnFromSpecifier(keyArgs) - : // Pass a function to take full control over field identity. - typeof keyArgs === "function" - ? keyArgs - : // Leave existing.keyFn unchanged if above cases fail. - existing.keyFn; + : isArray(keyArgs) ? keyArgsFnFromSpecifier(keyArgs) + // Pass a function to take full control over field identity. + : typeof keyArgs === "function" ? keyArgs + // Leave existing.keyFn unchanged if above cases fail. + : existing.keyFn; if (typeof read === "function") { existing.read = read; @@ -803,8 +788,9 @@ export class Policies { } if (storeFieldName === void 0) { - storeFieldName = fieldSpec.field - ? storeKeyNameFromField(fieldSpec.field, fieldSpec.variables) + storeFieldName = + fieldSpec.field ? + storeKeyNameFromField(fieldSpec.field, fieldSpec.variables) : getStoreKeyName(fieldName, argsFromFieldSpecifier(fieldSpec)); } @@ -817,8 +803,7 @@ export class Policies { // Make sure custom field names start with the actual field.name.value // of the field, so we can always figure out which properties of a // StoreObject correspond to which original field names. - return fieldName === fieldNameFromStoreName(storeFieldName) - ? storeFieldName + return fieldName === fieldNameFromStoreName(storeFieldName) ? storeFieldName : fieldName + ":" + storeFieldName; } @@ -856,9 +841,9 @@ export class Policies { options, context, context.store.getStorage( - isReference(objectOrReference) - ? objectOrReference.__ref - : objectOrReference, + isReference(objectOrReference) ? + objectOrReference.__ref + : objectOrReference, storeFieldName ) ); diff --git a/src/cache/inmemory/reactiveVars.ts b/src/cache/inmemory/reactiveVars.ts index bb7050f57ca..a0f662d9908 100644 --- a/src/cache/inmemory/reactiveVars.ts +++ b/src/cache/inmemory/reactiveVars.ts @@ -1,6 +1,5 @@ import type { OptimisticDependencyFunction } from "optimism"; -import { dep } from "optimism"; -import { Slot } from "@wry/context"; +import { dep, Slot } from "optimism"; import type { InMemoryCache } from "./inMemoryCache.js"; import type { ApolloCache } from "../../core/index.js"; diff --git a/src/cache/inmemory/readFromStore.ts b/src/cache/inmemory/readFromStore.ts index 9a9ddc1498c..d89876d85c9 100644 --- a/src/cache/inmemory/readFromStore.ts +++ b/src/cache/inmemory/readFromStore.ts @@ -28,6 +28,9 @@ import { isNonNullObject, canUseWeakMap, compact, + canonicalStringify, + cacheSizes, + defaultCacheSizes, } from "../../utilities/index.js"; import type { Cache } from "../core/types/Cache.js"; import type { @@ -50,7 +53,7 @@ import type { Policies } from "./policies.js"; import type { InMemoryCache } from "./inMemoryCache.js"; import type { MissingTree } from "../core/types/common.js"; import { MissingFieldError } from "../core/types/common.js"; -import { canonicalStringify, ObjectCanon } from "./object-canon.js"; +import { ObjectCanon } from "./object-canon.js"; export type VariableMap = { [name: string]: any }; @@ -152,6 +155,10 @@ export class StoreReader { this.canon = config.canon || new ObjectCanon(); + // memoized functions in this class will be "garbage-collected" + // by recreating the whole `StoreReader` in + // `InMemoryCache.resetResultsCache` + // (triggered from `InMemoryCache.gc` with `resetResultCache: true`) this.executeSelectionSet = wrap( (options) => { const { canonizeResults } = options.context; @@ -188,7 +195,10 @@ export class StoreReader { return this.execSelectionSetImpl(options); }, { - max: this.config.resultCacheMaxSize, + max: + this.config.resultCacheMaxSize || + cacheSizes["inMemoryCache.executeSelectionSet"] || + defaultCacheSizes["inMemoryCache.executeSelectionSet"], keyArgs: execSelectionSetKeyArgs, // Note that the parameters of makeCacheKey are determined by the // array returned by keyArgs. @@ -214,7 +224,10 @@ export class StoreReader { return this.execSubSelectedArrayImpl(options); }, { - max: this.config.resultCacheMaxSize, + max: + this.config.resultCacheMaxSize || + cacheSizes["inMemoryCache.executeSubSelectedArray"] || + defaultCacheSizes["inMemoryCache.executeSubSelectedArray"], makeCacheKey({ field, array, context }) { if (supportsResultCaching(context.store)) { return context.store.makeCacheKey(field, array, context.varString); @@ -227,9 +240,6 @@ export class StoreReader { /** * Given a store and a query, return as much of the result as possible and * identify if any data was missing from the store. - * @param {DocumentNode} query A parsed GraphQL query document - * @param {Store} store The Apollo Client store object - * @return {result: Object, complete: [boolean]} */ public diffQueryAgainstStore({ store, @@ -386,22 +396,24 @@ export class StoreReader { if (!addTypenameToDocument.added(selection)) { missing = missingMerger.merge(missing, { [resultName]: `Can't find field '${selection.name.value}' on ${ - isReference(objectOrReference) - ? objectOrReference.__ref + " object" - : "object " + JSON.stringify(objectOrReference, null, 2) + isReference(objectOrReference) ? + objectOrReference.__ref + " object" + : "object " + JSON.stringify(objectOrReference, null, 2) }`, }); } } else if (isArray(fieldValue)) { - fieldValue = handleMissing( - this.executeSubSelectedArray({ - field: selection, - array: fieldValue, - enclosingRef, - context, - }), - resultName - ); + if (fieldValue.length > 0) { + fieldValue = handleMissing( + this.executeSubSelectedArray({ + field: selection, + array: fieldValue, + enclosingRef, + context, + }), + resultName + ); + } } else if (!selection.selectionSet) { // If the field does not have a selection set, then we handle it // as a scalar value. To keep this.canon from canonicalizing @@ -446,11 +458,12 @@ export class StoreReader { const result = mergeDeepArray(objectsToMerge); const finalResult: ExecResult = { result, missing }; - const frozen = context.canonizeResults - ? this.canon.admit(finalResult) - : // Since this.canon is normally responsible for freezing results (only in + const frozen = + context.canonizeResults ? + this.canon.admit(finalResult) + // Since this.canon is normally responsible for freezing results (only in // development), freeze them manually if canonization is disabled. - maybeDeepFreeze(finalResult); + : maybeDeepFreeze(finalResult); // Store this result with its selection set so that we can quickly // recognize it again in the StoreReader#isFresh method. @@ -535,7 +548,7 @@ function firstMissing(tree: MissingTree): string | undefined { return value; }); } catch (result) { - return result; + return result as string; } } diff --git a/src/cache/inmemory/types.ts b/src/cache/inmemory/types.ts index 8a5bc71b583..10678ce5ad7 100644 --- a/src/cache/inmemory/types.ts +++ b/src/cache/inmemory/types.ts @@ -109,10 +109,23 @@ export type OptimisticStoreItem = { }; export type ReadQueryOptions = { + /** + * The Apollo Client store object. + */ store: NormalizedCache; + /** + * A parsed GraphQL query document. + */ query: DocumentNode; variables?: Object; previousResult?: any; + /** + * @deprecated + * Using `canonizeResults` can result in memory leaks so we generally do not + * recommend using this option anymore. + * A future version of Apollo Client will contain a similar feature without + * the risk of memory leaks. + */ canonizeResults?: boolean; rootId?: string; config?: ApolloReducerConfig; @@ -131,7 +144,17 @@ export interface InMemoryCacheConfig extends ApolloReducerConfig { resultCaching?: boolean; possibleTypes?: PossibleTypesMap; typePolicies?: TypePolicies; + /** + * @deprecated + * Please use `cacheSizes` instead. + */ resultCacheMaxSize?: number; + /** + * @deprecated + * Using `canonizeResults` can result in memory leaks so we generally do not + * recommend using this option anymore. + * A future version of Apollo Client will contain a similar feature. + */ canonizeResults?: boolean; fragments?: FragmentRegistryAPI; } diff --git a/src/cache/inmemory/writeToStore.ts b/src/cache/inmemory/writeToStore.ts index 1d3f3fb01d4..9e7daeb7ebe 100644 --- a/src/cache/inmemory/writeToStore.ts +++ b/src/cache/inmemory/writeToStore.ts @@ -25,6 +25,7 @@ import { addTypenameToDocument, isNonEmptyArray, argumentsObjectFromField, + canonicalStringify, } from "../../utilities/index.js"; import type { @@ -44,7 +45,6 @@ import type { StoreReader } from "./readFromStore.js"; import type { InMemoryCache } from "./inMemoryCache.js"; import type { EntityStore } from "./entityStore.js"; import type { Cache } from "../../core/index.js"; -import { canonicalStringify } from "./object-canon.js"; import { normalizeReadFieldOptions } from "./policies.js"; import type { ReadFieldFunction } from "../core/types/common.js"; @@ -95,13 +95,13 @@ function getContextFlavor( context.flavors.set( key, (flavored = - context.clientOnly === clientOnly && context.deferred === deferred - ? context - : { - ...context, - clientOnly, - deferred, - }) + context.clientOnly === clientOnly && context.deferred === deferred ? + context + : { + ...context, + clientOnly, + deferred, + }) ); } return flavored as TContext; @@ -330,9 +330,9 @@ export class StoreWriter { field, // Reset context.clientOnly and context.deferred to their default // values before processing nested selection sets. - field.selectionSet - ? getContextFlavor(context, false, false) - : context, + field.selectionSet ? + getContextFlavor(context, false, false) + : context, childTree ); @@ -633,13 +633,15 @@ export class StoreWriter { // Items in the same position in different arrays are not // necessarily related to each other, so when incoming is an array // we process its elements as if there was no existing data. - !isArray(incoming) && - // Likewise, existing must be either a Reference or a StoreObject - // in order for its fields to be safe to merge with the fields of - // the incoming object. - (isReference(existing) || storeValueIsStoreObject(existing)) - ? existing - : void 0; + ( + !isArray(incoming) && + // Likewise, existing must be either a Reference or a StoreObject + // in order for its fields to be safe to merge with the fields of + // the incoming object. + (isReference(existing) || storeValueIsStoreObject(existing)) + ) ? + existing + : void 0; // This narrowing is implied by mergeTree.map.size > 0 and // !isReference(incoming), though TypeScript understandably cannot @@ -665,11 +667,13 @@ export class StoreWriter { from: typeof e | typeof i, name: string | number ): StoreValue => { - return isArray(from) - ? typeof name === "number" - ? from[name] + return ( + isArray(from) ? + typeof name === "number" ? + from[name] : void 0 - : context.store.getFieldValue(from, String(name)); + : context.store.getFieldValue(from, String(name)) + ); }; mergeTree.map.forEach((childTree, storeFieldName) => { @@ -739,18 +743,17 @@ function mergeMergeTrees( if (!left || mergeTreeIsEmpty(left)) return right; const info = - left.info && right.info - ? { - ...left.info, - ...right.info, - } - : left.info || right.info; + left.info && right.info ? + { + ...left.info, + ...right.info, + } + : left.info || right.info; const needToMergeMaps = left.map.size && right.map.size; - const map = needToMergeMaps - ? new Map() - : left.map.size - ? left.map + const map = + needToMergeMaps ? new Map() + : left.map.size ? left.map : right.map; const merged = { info, map }; @@ -854,8 +857,8 @@ This could cause additional (usually avoidable) network requests to fetch data t To address this problem (which is not a bug in Apollo Client), %sdefine a custom merge function for the %s field, so InMemoryCache can safely merge these objects: - existing: %s - incoming: %s + existing: %o + incoming: %o For more information about these options, please refer to the documentation: @@ -864,13 +867,13 @@ For more information about these options, please refer to the documentation: `, fieldName, parentType, - childTypenames.length - ? "either ensure all objects of type " + - childTypenames.join(" and ") + - " have an ID or a custom merge function, or " - : "", + childTypenames.length ? + "either ensure all objects of type " + + childTypenames.join(" and ") + + " have an ID or a custom merge function, or " + : "", typeDotName, - existing, - incoming + { ...existing }, + { ...incoming } ); } diff --git a/src/core/ApolloClient.ts b/src/core/ApolloClient.ts index 12b9e7e0e0e..05e713d520a 100644 --- a/src/core/ApolloClient.ts +++ b/src/core/ApolloClient.ts @@ -43,38 +43,102 @@ export interface DefaultOptions { let hasSuggestedDevtools = false; -export type ApolloClientOptions = { +export interface ApolloClientOptions { + /** + * The URI of the GraphQL endpoint that Apollo Client will communicate with. + * + * One of `uri` or `link` is **required**. If you provide both, `link` takes precedence. + */ uri?: string | UriFunction; credentials?: string; + /** + * An object representing headers to include in every HTTP request, such as `{Authorization: 'Bearer 1234'}` + * + * This value will be ignored when using the `link` option. + */ headers?: Record; + /** + * You can provide an `ApolloLink` instance to serve as Apollo Client's network layer. For more information, see [Advanced HTTP networking](https://www.apollographql.com/docs/react/networking/advanced-http-networking/). + * + * One of `uri` or `link` is **required**. If you provide both, `link` takes precedence. + */ link?: ApolloLink; + /** + * The cache that Apollo Client should use to store query results locally. The recommended cache is `InMemoryCache`, which is provided by the `@apollo/client` package. + * + * For more information, see [Configuring the cache](https://www.apollographql.com/docs/react/caching/cache-configuration/). + */ cache: ApolloCache; + /** + * The time interval (in milliseconds) before Apollo Client force-fetches queries after a server-side render. + * + * @defaultValue `0` (no delay) + */ ssrForceFetchDelay?: number; + /** + * When using Apollo Client for [server-side rendering](https://www.apollographql.com/docs/react/performance/server-side-rendering/), set this to `true` so that the [`getDataFromTree` function](../react/ssr/#getdatafromtree) can work effectively. + * + * @defaultValue `false` + */ ssrMode?: boolean; + /** + * If `true`, the [Apollo Client Devtools](https://www.apollographql.com/docs/react/development-testing/developer-tooling/#apollo-client-devtools) browser extension can connect to Apollo Client. + * + * The default value is `false` in production and `true` in development (if there is a `window` object). + */ connectToDevTools?: boolean; + /** + * If `false`, Apollo Client sends every created query to the server, even if a _completely_ identical query (identical in terms of query string, variable values, and operationName) is already in flight. + * + * @defaultValue `true` + */ queryDeduplication?: boolean; + /** + * Provide this object to set application-wide default values for options you can provide to the `watchQuery`, `query`, and `mutate` functions. See below for an example object. + * + * See this [example object](https://www.apollographql.com/docs/react/api/core/ApolloClient#example-defaultoptions-object). + */ defaultOptions?: DefaultOptions; + defaultContext?: Partial; + /** + * If `true`, Apollo Client will assume results read from the cache are never mutated by application code, which enables substantial performance optimizations. + * + * @defaultValue `false` + */ assumeImmutableResults?: boolean; resolvers?: Resolvers | Resolvers[]; typeDefs?: string | string[] | DocumentNode | DocumentNode[]; fragmentMatcher?: FragmentMatcher; + /** + * A custom name (e.g., `iOS`) that identifies this particular client among your set of clients. Apollo Server and Apollo Studio use this property as part of the [client awareness](https://www.apollographql.com/docs/apollo-server/monitoring/metrics#identifying-distinct-clients) feature. + */ name?: string; + /** + * A custom version that identifies the current version of this particular client (e.g., `1.2`). Apollo Server and Apollo Studio use this property as part of the [client awareness](https://www.apollographql.com/docs/apollo-server/monitoring/metrics#identifying-distinct-clients) feature. + * + * This is **not** the version of Apollo Client that you are using, but rather any version string that helps you differentiate between versions of your client. + */ version?: string; documentTransform?: DocumentTransform; -}; +} // Though mergeOptions now resides in @apollo/client/utilities, it was // previously declared and exported from this module, and then reexported from // @apollo/client/core. Since we need to preserve that API anyway, the easiest // solution is to reexport mergeOptions where it was previously declared (here). import { mergeOptions } from "../utilities/index.js"; +import { getApolloClientMemoryInternals } from "../utilities/caching/getMemoryInternals.js"; +import type { + WatchFragmentOptions, + WatchFragmentResult, +} from "../cache/core/cache.js"; export { mergeOptions }; /** * This is the primary Apollo Client class. It is used to send GraphQL documents (i.e. queries - * and mutations) to a GraphQL spec-compliant server over a {@link NetworkInterface} instance, + * and mutations) to a GraphQL spec-compliant server over an `ApolloLink` instance, * receive results from the server and cache the results in a store. It also delivers updates - * to GraphQL queries through {@link Observable} instances. + * to GraphQL queries through `Observable` instances. */ export class ApolloClient implements DataProxy { public link: ApolloLink; @@ -86,44 +150,36 @@ export class ApolloClient implements DataProxy { public readonly typeDefs: ApolloClientOptions["typeDefs"]; private queryManager: QueryManager; - private devToolsHookCb: Function; + private devToolsHookCb?: Function; private resetStoreCallbacks: Array<() => Promise> = []; private clearStoreCallbacks: Array<() => Promise> = []; private localState: LocalState; /** - * Constructs an instance of {@link ApolloClient}. - * - * @param uri The GraphQL endpoint that Apollo Client will connect to. If - * `link` is configured, this option is ignored. - * @param link The {@link ApolloLink} over which GraphQL documents will be resolved into a response. - * - * @param cache The initial cache to use in the data store. - * - * @param ssrMode Determines whether this is being run in Server Side Rendering (SSR) mode. + * Constructs an instance of `ApolloClient`. * - * @param ssrForceFetchDelay Determines the time interval before we force fetch queries for a - * server side render. + * @example + * ```js + * import { ApolloClient, InMemoryCache } from '@apollo/client'; * - * @param queryDeduplication If set to false, a query will still be sent to the server even if a query - * with identical parameters (query, variables, operationName) is already in flight. + * const cache = new InMemoryCache(); * - * @param defaultOptions Used to set application wide defaults for the - * options supplied to `watchQuery`, `query`, or - * `mutate`. + * const client = new ApolloClient({ + * // Provide required constructor fields + * cache: cache, + * uri: 'http://localhost:4000/', * - * @param assumeImmutableResults When this option is true, the client will assume results - * read from the cache are never mutated by application code, - * which enables substantial performance optimizations. - * - * @param name A custom name that can be used to identify this client, when - * using Apollo client awareness features. E.g. "iOS". - * - * @param version A custom version that can be used to identify this client, - * when using Apollo client awareness features. This is the - * version of your client, which you may want to increment on - * new builds. This is NOT the version of Apollo Client that - * you are using. + * // Provide some optional constructor fields + * name: 'react-web-client', + * version: '1.3', + * queryDeduplication: false, + * defaultOptions: { + * watchQuery: { + * fetchPolicy: 'cache-and-network', + * }, + * }, + * }); + * ``` */ constructor(options: ApolloClientOptions) { if (!options.cache) { @@ -150,6 +206,7 @@ export class ApolloClient implements DataProxy { __DEV__, queryDeduplication = true, defaultOptions, + defaultContext, assumeImmutableResults = cache.assumeImmutableResults, resolvers, typeDefs, @@ -161,9 +218,8 @@ export class ApolloClient implements DataProxy { let { link } = options; if (!link) { - link = uri - ? new HttpLink({ uri, credentials, headers }) - : ApolloLink.empty(); + link = + uri ? new HttpLink({ uri, credentials, headers }) : ApolloLink.empty(); } this.link = link; @@ -183,6 +239,7 @@ export class ApolloClient implements DataProxy { this.watchQuery = this.watchQuery.bind(this); this.query = this.query.bind(this); this.mutate = this.mutate.bind(this); + this.watchFragment = this.watchFragment.bind(this); this.resetStore = this.resetStore.bind(this); this.reFetchObservableQueries = this.reFetchObservableQueries.bind(this); @@ -199,6 +256,7 @@ export class ApolloClient implements DataProxy { cache: this.cache, link: this.link, defaultOptions: this.defaultOptions, + defaultContext, documentTransform, queryDeduplication, ssrMode, @@ -208,8 +266,9 @@ export class ApolloClient implements DataProxy { }, localState: this.localState, assumeImmutableResults, - onBroadcast: connectToDevTools - ? () => { + onBroadcast: + connectToDevTools ? + () => { if (this.devToolsHookCb) { this.devToolsHookCb({ action: {}, @@ -254,7 +313,8 @@ export class ApolloClient implements DataProxy { typeof window !== "undefined" && window.document && window.top === window.self && - !(window as any).__APOLLO_DEVTOOLS_GLOBAL_HOOK__ + !(window as any).__APOLLO_DEVTOOLS_GLOBAL_HOOK__ && + /^(https?|file):$/.test(window.location.protocol) ) { const nav = window.navigator; const ua = nav && nav.userAgent; @@ -300,8 +360,8 @@ export class ApolloClient implements DataProxy { /** * This watches the cache store of the query according to the options specified and - * returns an {@link ObservableQuery}. We can subscribe to this {@link ObservableQuery} and - * receive updated results through a GraphQL observer when the cache store changes. + * returns an `ObservableQuery`. We can subscribe to this `ObservableQuery` and + * receive updated results through an observer when the cache store changes. * * Note that this method is not an implementation of GraphQL subscriptions. Rather, * it uses Apollo's store in order to reactively deliver updates to your query results. @@ -339,10 +399,10 @@ export class ApolloClient implements DataProxy { /** * This resolves a single query according to the options specified and - * returns a {@link Promise} which is either resolved with the resulting data + * returns a `Promise` which is either resolved with the resulting data * or rejected with an error. * - * @param options An object of type {@link QueryOptions} that allows us to + * @param options - An object of type `QueryOptions` that allows us to * describe how this query should be treated e.g. whether it should hit the * server at all or just resolve from the cache, etc. */ @@ -371,8 +431,9 @@ export class ApolloClient implements DataProxy { /** * This resolves a single mutation according to the options specified and returns a - * {@link Promise} which is either resolved with the resulting data or rejected with an - * error. + * Promise which is either resolved with the resulting data or rejected with an + * error. In some cases both `data` and `errors` might be undefined, for example + * when `errorPolicy` is set to `'ignore'`. * * It takes options as an object with the following keys and values: */ @@ -394,7 +455,7 @@ export class ApolloClient implements DataProxy { /** * This subscribes to a graphql subscription according to the options specified and returns an - * {@link Observable} which either emits received data or an error. + * `Observable` which either emits received data or an error. */ public subscribe< T = any, @@ -409,7 +470,7 @@ export class ApolloClient implements DataProxy { * the root query. To start at a specific id returned by `dataIdFromObject` * use `readFragment`. * - * @param optimistic Set to `true` to allow `readQuery` to return + * @param optimistic - Set to `true` to allow `readQuery` to return * optimistic results. Is `false` by default. */ public readQuery( @@ -419,6 +480,32 @@ export class ApolloClient implements DataProxy { return this.cache.readQuery(options, optimistic); } + /** + * Watches the cache store of the fragment according to the options specified + * and returns an `Observable`. We can subscribe to this + * `Observable` and receive updated results through an + * observer when the cache store changes. + * + * You must pass in a GraphQL document with a single fragment or a document + * with multiple fragments that represent what you are reading. If you pass + * in a document with multiple fragments then you must also specify a + * `fragmentName`. + * + * @since 3.10.0 + * @param options - An object of type `WatchFragmentOptions` that allows + * the cache to identify the fragment and optionally specify whether to react + * to optimistic updates. + */ + + public watchFragment< + TFragmentData = unknown, + TVariables = OperationVariables, + >( + options: WatchFragmentOptions + ): Observable> { + return this.cache.watchFragment(options); + } + /** * Tries to read some data from the store in the shape of the provided * GraphQL fragment without making a network request. This method will read a @@ -430,7 +517,7 @@ export class ApolloClient implements DataProxy { * in a document with multiple fragments then you must also specify a * `fragmentName`. * - * @param optimistic Set to `true` to allow `readFragment` to return + * @param optimistic - Set to `true` to allow `readFragment` to return * optimistic results. Is `false` by default. */ public readFragment( @@ -592,7 +679,9 @@ export class ApolloClient implements DataProxy { >( options: RefetchQueriesOptions ): RefetchQueriesResult { - const map = this.queryManager.refetchQueries(options); + const map = this.queryManager.refetchQueries( + options as RefetchQueriesOptions, TResult> + ); const queries: ObservableQuery[] = []; const results: InternalRefetchQueriesResult[] = []; @@ -692,4 +781,94 @@ export class ApolloClient implements DataProxy { public setLink(newLink: ApolloLink) { this.link = this.queryManager.link = newLink; } + + public get defaultContext() { + return this.queryManager.defaultContext; + } + + /** + * @experimental + * This is not a stable API - it is used in development builds to expose + * information to the DevTools. + * Use at your own risk! + * For more details, see [Memory Management](https://www.apollographql.com/docs/react/caching/memory-management/#measuring-cache-usage) + * + * @example + * ```ts + * console.log(client.getMemoryInternals()) + * ``` + * Logs output in the following JSON format: + * @example + * ```json + *{ + * limits: { + * parser: 1000, + * canonicalStringify: 1000, + * print: 2000, + * 'documentTransform.cache': 2000, + * 'queryManager.getDocumentInfo': 2000, + * 'PersistedQueryLink.persistedQueryHashes': 2000, + * 'fragmentRegistry.transform': 2000, + * 'fragmentRegistry.lookup': 1000, + * 'fragmentRegistry.findFragmentSpreads': 4000, + * 'cache.fragmentQueryDocuments': 1000, + * 'removeTypenameFromVariables.getVariableDefinitions': 2000, + * 'inMemoryCache.maybeBroadcastWatch': 5000, + * 'inMemoryCache.executeSelectionSet': 10000, + * 'inMemoryCache.executeSubSelectedArray': 5000 + * }, + * sizes: { + * parser: 26, + * canonicalStringify: 4, + * print: 14, + * addTypenameDocumentTransform: [ + * { + * cache: 14, + * }, + * ], + * queryManager: { + * getDocumentInfo: 14, + * documentTransforms: [ + * { + * cache: 14, + * }, + * { + * cache: 14, + * }, + * ], + * }, + * fragmentRegistry: { + * findFragmentSpreads: 34, + * lookup: 20, + * transform: 14, + * }, + * cache: { + * fragmentQueryDocuments: 22, + * }, + * inMemoryCache: { + * executeSelectionSet: 4345, + * executeSubSelectedArray: 1206, + * maybeBroadcastWatch: 32, + * }, + * links: [ + * { + * PersistedQueryLink: { + * persistedQueryHashes: 14, + * }, + * }, + * { + * removeTypenameFromVariables: { + * getVariableDefinitions: 14, + * }, + * }, + * ], + * }, + * } + *``` + */ + public getMemoryInternals?: typeof getApolloClientMemoryInternals; +} + +if (__DEV__) { + ApolloClient.prototype.getMemoryInternals = getApolloClientMemoryInternals; } diff --git a/src/core/LocalState.ts b/src/core/LocalState.ts index 6f0aaf61f08..4d354349099 100644 --- a/src/core/LocalState.ts +++ b/src/core/LocalState.ts @@ -75,9 +75,9 @@ export type LocalStateOptions = { export class LocalState { private cache: ApolloCache; - private client: ApolloClient; + private client?: ApolloClient; private resolvers?: Resolvers; - private fragmentMatcher: FragmentMatcher; + private fragmentMatcher?: FragmentMatcher; private selectionsToResolveCache = new WeakMap< ExecutableDefinitionNode, Set @@ -162,7 +162,7 @@ export class LocalState { this.fragmentMatcher = fragmentMatcher; } - public getFragmentMatcher(): FragmentMatcher { + public getFragmentMatcher(): FragmentMatcher | undefined { return this.fragmentMatcher; } @@ -197,11 +197,11 @@ export class LocalState { // To support `@client @export(as: "someVar")` syntax, we'll first resolve // @client @export fields locally, then pass the resolved values back to be // used alongside the original operation variables. - public async addExportedVariables( + public async addExportedVariables( document: DocumentNode, - variables: OperationVariables = {}, + variables: TVars = {} as TVars, context = {} - ) { + ): /* returns at least the variables that were passed in */ Promise { if (document) { return this.resolveDocument( document, @@ -274,8 +274,9 @@ export class LocalState { const definitionOperation = mainDefinition.operation; - const defaultOperationType = definitionOperation - ? definitionOperation.charAt(0).toUpperCase() + + const defaultOperationType = + definitionOperation ? + definitionOperation.charAt(0).toUpperCase() + definitionOperation.slice(1) : "Query"; diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index 57f0867276f..47deef22483 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -35,6 +35,7 @@ import type { QueryInfo } from "./QueryInfo.js"; import type { MissingFieldError } from "../cache/index.js"; import type { MissingTree } from "../cache/core/types/common.js"; import { equalByQuery } from "./equalByQuery.js"; +import type { TODO } from "../utilities/types/TODO.js"; const { assign, hasOwnProperty } = Object; @@ -79,6 +80,9 @@ export class ObservableQuery< // Computed shorthand for this.options.variables, preserved for // backwards compatibility. + /** + * An object containing the variables that were provided for the query. + */ public get variables(): TVariables | undefined { return this.options.variables; } @@ -166,9 +170,9 @@ export class ObservableQuery< const { fetchPolicy = defaultFetchPolicy, // Make sure we don't store "standby" as the initialFetchPolicy. - initialFetchPolicy = fetchPolicy === "standby" - ? defaultFetchPolicy - : fetchPolicy, + initialFetchPolicy = fetchPolicy === "standby" ? defaultFetchPolicy : ( + fetchPolicy + ), } = options; this.options = { @@ -224,6 +228,11 @@ export class ObservableQuery< }); } + /** @internal */ + public resetDiff() { + this.queryInfo.resetDiff(); + } + public getCurrentResult(saveAsLastResult = true): ApolloQueryResult { // Use the last result as long as the variables match this.variables. const lastResult = this.getLastResult(true); @@ -316,9 +325,9 @@ export class ObservableQuery< return true; } - const resultIsDifferent = this.queryManager.getDocumentInfo(this.query) - .hasNonreactiveDirective - ? !equalByQuery(this.query, this.last.result, newResult, this.variables) + const resultIsDifferent = + this.queryManager.getDocumentInfo(this.query).hasNonreactiveDirective ? + !equalByQuery(this.query, this.last.result, newResult, this.variables) : !equal(this.last.result, newResult); return ( @@ -363,7 +372,7 @@ export class ObservableQuery< * Update the variables of this observable query, and fetch the new results. * This method should be preferred over `setVariables` in most use cases. * - * @param variables: The new set of variables. If there are missing variables, + * @param variables - The new set of variables. If there are missing variables, * the previous values of those variables will be used. */ public refetch( @@ -411,6 +420,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, return this.reobserve(reobserveOptions, NetworkStatus.refetch); } + /** + * A function that helps you fetch the next set of results for a [paginated list field](https://www.apollographql.com/docs/react/pagination/core-api/). + */ public fetchMore< TFetchData = TData, TFetchVars extends OperationVariables = TVariables, @@ -426,17 +438,17 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, } ): Promise> { const combinedOptions = { - ...(fetchMoreOptions.query - ? fetchMoreOptions - : { - ...this.options, - query: this.options.query, - ...fetchMoreOptions, - variables: { - ...this.options.variables, - ...fetchMoreOptions.variables, - }, - }), + ...(fetchMoreOptions.query ? fetchMoreOptions : ( + { + ...this.options, + query: this.options.query, + ...fetchMoreOptions, + variables: { + ...this.options.variables, + ...fetchMoreOptions.variables, + }, + } + )), // The fetchMore request goes immediately to the network and does // not automatically write its result to the cache (hence no-cache // instead of network-only), because we allow the caller of @@ -454,8 +466,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, // pagination. We will however run the transforms on the original document // as well as the document passed in `fetchMoreOptions` to ensure the cache // uses the most up-to-date document which may rely on runtime conditionals. - this.lastQuery = fetchMoreOptions.query - ? this.transformDocument(this.options.query) + this.lastQuery = + fetchMoreOptions.query ? + this.transformDocument(this.options.query) : combinedOptions.query; // Simulate a loading result for the original query with @@ -538,6 +551,11 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, // XXX the subscription variables are separate from the query variables. // if you want to update subscription variables, right now you have to do that separately, // and you can only do it by stopping the subscription and then subscribing again with new variables. + /** + * A function that enables you to execute a [subscription](https://www.apollographql.com/docs/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query. + * + * This function returns _another_ function that you can call to terminate the subscription. + */ public subscribeToMore< TSubscriptionData = TData, TSubscriptionVariables extends OperationVariables = TVariables, @@ -613,9 +631,7 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, * Note: the promise will return null immediately if the query is not active * (there are no subscribers). * - * @private - * - * @param variables: The new set of variables. If there are missing variables, + * @param variables - The new set of variables. If there are missing variables, * the previous values of those variables will be used. */ public setVariables( @@ -645,6 +661,11 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, ); } + /** + * A function that enables you to update the query's cached result without executing a followup GraphQL operation. + * + * See [using updateQuery and updateFragment](https://www.apollographql.com/docs/react/caching/cache-interaction/#using-updatequery-and-updatefragment) for additional information. + */ public updateQuery( mapFn: ( previousQueryResult: TData, @@ -674,11 +695,17 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, } } + /** + * A function that instructs the query to begin re-executing at a specified interval (in milliseconds). + */ public startPolling(pollInterval: number) { this.options.pollInterval = pollInterval; this.updatePolling(); } + /** + * A function that instructs the query to stop polling after a previous call to `startPolling`. + */ public stopPolling() { this.options.pollInterval = 0; this.updatePolling(); @@ -754,7 +781,7 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, options: { pollInterval }, } = this; - if (!pollInterval) { + if (!pollInterval || !this.hasObservers()) { if (pollingInfo) { clearTimeout(pollingInfo.timeout); delete this.pollingInfo; @@ -776,7 +803,10 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, const maybeFetch = () => { if (this.pollingInfo) { - if (!isNetworkRequestInFlight(this.queryInfo.networkStatus)) { + if ( + !isNetworkRequestInFlight(this.queryInfo.networkStatus) && + !this.options.skipPollAttempt?.() + ) { this.reobserve( { // Most fetchPolicy options don't make sense to use in a polling context, as @@ -784,9 +814,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, // no-cache are both useful for when the user wants to control whether or not the // polled results are written to the cache. fetchPolicy: - this.options.initialFetchPolicy === "no-cache" - ? "no-cache" - : "network-only", + this.options.initialFetchPolicy === "no-cache" ? + "no-cache" + : "network-only", }, NetworkStatus.poll ).then(poll, poll); @@ -817,8 +847,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, error = void 0; } return (this.last = { - result: this.queryManager.assumeImmutableResults - ? newResult + result: + this.queryManager.assumeImmutableResults ? + newResult : cloneDeep(newResult), variables, ...(error ? { error } : null), @@ -848,8 +879,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, const oldFetchPolicy = this.options.fetchPolicy; const mergedOptions = compact(this.options, newOptions || {}); - const options = useDisposableConcast - ? // Disposable Concast fetches receive a shallow copy of this.options + const options = + useDisposableConcast ? + // Disposable Concast fetches receive a shallow copy of this.options // (merged with newOptions), leaving this.options unmodified. mergedOptions : assign(this.options, mergedOptions); @@ -896,12 +928,16 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, const { concast, fromLink } = this.fetch(options, newNetworkStatus, query); const observer: Observer> = { next: (result) => { - finishWaitingForOwnResult(); - this.reportResult(result, variables); + if (equal(this.variables, variables)) { + finishWaitingForOwnResult(); + this.reportResult(result, variables); + } }, error: (error) => { - finishWaitingForOwnResult(); - this.reportError(error, variables); + if (equal(this.variables, variables)) { + finishWaitingForOwnResult(); + this.reportError(error, variables); + } }, }; @@ -924,8 +960,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`, public reobserve( newOptions?: Partial>, newNetworkStatus?: NetworkStatus - ) { - return this.reobserveAsConcast(newOptions, newNetworkStatus).promise; + ): Promise> { + return this.reobserveAsConcast(newOptions, newNetworkStatus) + .promise as TODO; } public resubscribeAfterError( @@ -1048,14 +1085,18 @@ export function reobserveCacheFirst( fetchPolicy: "cache-first", // Use a temporary nextFetchPolicy function that replaces itself with the // previous nextFetchPolicy value and returns the original fetchPolicy. - nextFetchPolicy(this: WatchQueryOptions) { + nextFetchPolicy( + this: WatchQueryOptions, + currentFetchPolicy: WatchQueryFetchPolicy, + context: NextFetchPolicyContext + ) { // Replace this nextFetchPolicy function in the options object with the // original this.options.nextFetchPolicy value. this.nextFetchPolicy = nextFetchPolicy; // If the original nextFetchPolicy value was a function, give it a // chance to decide what happens here. - if (typeof nextFetchPolicy === "function") { - return nextFetchPolicy.apply(this, arguments); + if (typeof this.nextFetchPolicy === "function") { + return this.nextFetchPolicy(currentFetchPolicy, context); } // Otherwise go back to the original this.options.fetchPolicy. return fetchPolicy!; diff --git a/src/core/QueryInfo.ts b/src/core/QueryInfo.ts index 1b6f17acc5a..2e8c149eedd 100644 --- a/src/core/QueryInfo.ts +++ b/src/core/QueryInfo.ts @@ -7,7 +7,7 @@ import { mergeIncrementalData } from "../utilities/index.js"; import type { WatchQueryOptions, ErrorPolicy } from "./watchQueryOptions.js"; import type { ObservableQuery } from "./ObservableQuery.js"; import { reobserveCacheFirst } from "./ObservableQuery.js"; -import type { QueryListener, MethodKeys } from "./types.js"; +import type { QueryListener } from "./types.js"; import type { FetchResult } from "../link/core/index.js"; import { isNonEmptyArray, @@ -36,10 +36,11 @@ const destructiveMethodCounts = new (canUseWeakMap ? WeakMap : Map)< function wrapDestructiveCacheMethod( cache: ApolloCache, - methodName: MethodKeys> + methodName: "evict" | "modify" | "reset" ) { const original = cache[methodName]; if (typeof original === "function") { + // @ts-expect-error this is just too generic to be typed correctly cache[methodName] = function () { destructiveMethodCounts.set( cache, @@ -49,6 +50,7 @@ function wrapDestructiveCacheMethod( // that matters in any conceivable practical scenario. (destructiveMethodCounts.get(cache)! + 1) % 1e15 ); + // @ts-expect-error this is just too generic to be typed correctly return original.apply(this, arguments); }; } @@ -111,7 +113,7 @@ export class QueryInfo { // NetworkStatus.loading, but also possibly fetchMore, poll, refetch, // or setVariables. networkStatus?: NetworkStatus; - observableQuery?: ObservableQuery; + observableQuery?: ObservableQuery; lastRequestId?: number; }): this { let networkStatus = query.networkStatus || NetworkStatus.loading; @@ -155,6 +157,10 @@ export class QueryInfo { this.dirty = false; } + resetDiff() { + this.lastDiff = void 0; + } + getDiff(): Cache.DiffResult { const options = this.getDiffOptions(); @@ -183,8 +189,9 @@ export class QueryInfo { diff: Cache.DiffResult | null, options?: Cache.DiffOptions ) { - this.lastDiff = diff - ? { + this.lastDiff = + diff ? + { diff, options: options || this.getDiffOptions(), } @@ -203,7 +210,22 @@ export class QueryInfo { setDiff(diff: Cache.DiffResult | null) { const oldDiff = this.lastDiff && this.lastDiff.diff; + + // If we are trying to deliver an incomplete cache result, we avoid + // reporting it if the query has errored, otherwise we let the broadcast try + // and repair the partial result by refetching the query. This check avoids + // a situation where a query that errors and another succeeds with + // overlapping data does not report the partial data result to the errored + // query. + // + // See https://github.com/apollographql/apollo-client/issues/11400 for more + // information on this issue. + if (diff && !diff.complete && this.observableQuery?.getLastError()) { + return; + } + this.updateLastDiff(diff); + if (!this.dirty && !equal(oldDiff && oldDiff.result, diff && diff.result)) { this.dirty = true; if (!this.notifyTimeout) { @@ -212,10 +234,10 @@ export class QueryInfo { } } - public readonly observableQuery: ObservableQuery | null = null; + public readonly observableQuery: ObservableQuery | null = null; private oqListener?: QueryListener; - setObservableQuery(oq: ObservableQuery | null) { + setObservableQuery(oq: ObservableQuery | null) { if (oq === this.observableQuery) return; if (this.oqListener) { @@ -359,9 +381,8 @@ export class QueryInfo { cacheWriteBehavior: CacheWriteBehavior ) { const merger = new DeepMerger(); - const graphQLErrors = isNonEmptyArray(result.errors) - ? result.errors.slice(0) - : []; + const graphQLErrors = + isNonEmptyArray(result.errors) ? result.errors.slice(0) : []; // Cancel the pending notify timeout (if it exists) to prevent extraneous network // requests. To allow future notify timeouts, diff and dirty are reset as well. diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index f57d7afda6c..92029d9a6f1 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 { + defaultCacheSizes, hasDirectives, isExecutionPatchIncrementalResult, isExecutionPatchResult, @@ -27,7 +28,6 @@ import { hasClientExports, graphQLResultHasError, getGraphQLErrorsFromResult, - canUseWeakMap, Observable, asyncMap, isNonEmptyArray, @@ -62,6 +62,7 @@ import type { InternalRefetchQueriesOptions, InternalRefetchQueriesResult, InternalRefetchQueriesMap, + DefaultContext, } from "./types.js"; import { LocalState } from "./LocalState.js"; @@ -74,9 +75,13 @@ import { import type { ApolloErrorOptions } from "../errors/index.js"; import { PROTOCOL_ERRORS_SYMBOL } from "../errors/index.js"; import { print } from "../utilities/index.js"; +import type { IgnoreModifier } from "../cache/core/types/common.js"; +import type { TODO } from "../utilities/types/TODO.js"; const { hasOwnProperty } = Object.prototype; +const IGNORE: IgnoreModifier = Object.create(null); + interface MutationStoreValue { mutation: DocumentNode; variables: Record; @@ -97,6 +102,8 @@ interface TransformCacheEntry { } import type { DefaultOptions } from "./ApolloClient.js"; +import { Trie } from "@wry/trie"; +import { AutoCleanedWeakCache, cacheSizes } from "../utilities/index.js"; export class QueryManager { public cache: ApolloCache; @@ -106,6 +113,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 +145,7 @@ export class QueryManager { clientAwareness = {}, localState, assumeImmutableResults = !!cache.assumeImmutableResults, + defaultContext, }: { cache: ApolloCache; link: ApolloLink; @@ -148,6 +157,7 @@ export class QueryManager { clientAwareness?: Record; localState?: LocalState; assumeImmutableResults?: boolean; + defaultContext?: Partial; }) { const defaultDocumentTransform = new DocumentTransform( (document) => this.cache.transformDocument(document), @@ -163,8 +173,9 @@ export class QueryManager { this.localState = localState || new LocalState({ cache }); this.ssrMode = ssrMode; this.assumeImmutableResults = assumeImmutableResults; - this.documentTransform = documentTransform - ? defaultDocumentTransform + this.documentTransform = + documentTransform ? + defaultDocumentTransform .concat(documentTransform) // The custom document transform may add new fragment spreads or new // field selections, so we want to give the cache a chance to run @@ -172,6 +183,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); @@ -251,7 +263,8 @@ export class QueryManager { error: null, } as MutationStoreValue); - if (optimisticResponse) { + const isOptimistic = + optimisticResponse && this.markMutationOptimistic( optimisticResponse, { @@ -266,7 +279,6 @@ export class QueryManager { keepRootFields, } ); - } this.broadcastQueries(); @@ -278,7 +290,7 @@ export class QueryManager { mutation, { ...context, - optimisticResponse, + optimisticResponse: isOptimistic ? optimisticResponse : void 0, }, variables, false @@ -318,7 +330,7 @@ export class QueryManager { updateQueries, awaitRefetchQueries, refetchQueries, - removeOptimistic: optimisticResponse ? mutationId : void 0, + removeOptimistic: isOptimistic ? mutationId : void 0, onQueryUpdated, keepRootFields, }); @@ -343,18 +355,18 @@ export class QueryManager { mutationStoreValue.error = err; } - if (optimisticResponse) { + if (isOptimistic) { self.cache.removeOptimistic(mutationId); } self.broadcastQueries(); reject( - err instanceof ApolloError - ? err - : new ApolloError({ - networkError: err, - }) + err instanceof ApolloError ? err : ( + new ApolloError({ + networkError: err, + }) + ) ); }, }); @@ -471,7 +483,7 @@ export class QueryManager { if ( cacheWrites.length > 0 || - mutation.refetchQueries || + (mutation.refetchQueries || "").length > 0 || mutation.update || mutation.onQueryUpdated || mutation.removeOptimistic @@ -479,7 +491,7 @@ export class QueryManager { const results: any[] = []; this.refetchQueries({ - updateCache: (cache: TCache) => { + updateCache: (cache) => { if (!skipCache) { cacheWrites.forEach((write) => cache.write(write)); } @@ -526,7 +538,7 @@ export class QueryManager { // either a SingleExecutionResult or the final ExecutionPatchResult, // call the update function. if (isFinalResult) { - update(cache, result, { + update(cache as TCache, result, { context: mutation.context, variables: mutation.variables, }); @@ -592,11 +604,15 @@ export class QueryManager { } ) { const data = - typeof optimisticResponse === "function" - ? optimisticResponse(mutation.variables) - : optimisticResponse; + typeof optimisticResponse === "function" ? + optimisticResponse(mutation.variables, { IGNORE }) + : optimisticResponse; - return this.cache.recordOptimisticTransaction((cache) => { + if (data === IGNORE) { + return false; + } + + this.cache.recordOptimisticTransaction((cache) => { try { this.markMutationResult( { @@ -609,6 +625,8 @@ export class QueryManager { invariant.error(error); } }, mutation.mutationId); + + return true; } public fetchQuery( @@ -617,7 +635,7 @@ export class QueryManager { networkStatus?: NetworkStatus ): Promise> { return this.fetchConcastWithInfo(queryId, options, networkStatus).concast - .promise; + .promise as TODO; } public getQueryStore() { @@ -645,10 +663,13 @@ export class QueryManager { return this.documentTransform.transformDocument(document); } - private transformCache = new (canUseWeakMap ? WeakMap : Map)< + private transformCache = new AutoCleanedWeakCache< DocumentNode, TransformCacheEntry - >(); + >( + cacheSizes["queryManager.getDocumentInfo"] || + defaultCacheSizes["queryManager.getDocumentInfo"] + ); public getDocumentInfo(document: DocumentNode) { const { transformCache } = this; @@ -914,9 +935,9 @@ export class QueryManager { queryNamesAndDocs.forEach((included, nameOrDoc) => { if (!included) { invariant.warn( - typeof nameOrDoc === "string" - ? `Unknown query named "%s" requested in refetchQueries options.include array` - : `Unknown query %s requested in refetchQueries options.include array`, + typeof nameOrDoc === "string" ? + `Unknown query named "%s" requested in refetchQueries options.include array` + : `Unknown query %o requested in refetchQueries options.include array`, nameOrDoc ); } @@ -1059,10 +1080,9 @@ export class QueryManager { // Use protected instead of private field so // @apollo/experimental-nextjs-app-support can access type info. - protected inFlightLinkObservables = new Map< - string, - Map> - >(); + protected inFlightLinkObservables = new Trie<{ + observable?: Observable>; + }>(false); private getObservableFromLink( query: DocumentNode, @@ -1072,7 +1092,7 @@ export class QueryManager { deduplication: boolean = context?.queryDeduplication ?? this.queryDeduplication ): Observable> { - let observable: Observable>; + let observable: Observable> | undefined; const { serverQuery, clientQuery } = this.getDocumentInfo(query); if (serverQuery) { @@ -1092,24 +1112,22 @@ export class QueryManager { if (deduplication) { const printedServerQuery = print(serverQuery); - const byVariables = - inFlightLinkObservables.get(printedServerQuery) || new Map(); - inFlightLinkObservables.set(printedServerQuery, byVariables); - const varJson = canonicalStringify(variables); - observable = byVariables.get(varJson); + const entry = inFlightLinkObservables.lookup( + printedServerQuery, + varJson + ); + + observable = entry.observable; if (!observable) { const concast = new Concast([ execute(link, operation) as Observable>, ]); - - byVariables.set(varJson, (observable = concast)); + observable = entry.observable = concast; concast.beforeNext(() => { - if (byVariables.delete(varJson) && byVariables.size < 1) { - inFlightLinkObservables.delete(printedServerQuery); - } + inFlightLinkObservables.remove(printedServerQuery, varJson); }); } } else { @@ -1200,9 +1218,10 @@ export class QueryManager { }, (networkError) => { - const error = isApolloError(networkError) - ? networkError - : new ApolloError({ networkError }); + const error = + isApolloError(networkError) ? networkError : ( + new ApolloError({ networkError }) + ); // Avoid storing errors from older interrupted queries. if (requestId >= queryInfo.lastRequestId) { @@ -1564,14 +1583,15 @@ export class QueryManager { }; const cacheWriteBehavior = - fetchPolicy === "no-cache" - ? CacheWriteBehavior.FORBID - : // Watched queries must opt into overwriting existing data on refetch, + fetchPolicy === "no-cache" ? CacheWriteBehavior.FORBID + // Watched queries must opt into overwriting existing data on refetch, // by passing refetchWritePolicy: "overwrite" in their WatchQueryOptions. + : ( networkStatus === NetworkStatus.refetch && - refetchWritePolicy !== "merge" - ? CacheWriteBehavior.OVERWRITE - : CacheWriteBehavior.MERGE; + refetchWritePolicy !== "merge" + ) ? + CacheWriteBehavior.OVERWRITE + : CacheWriteBehavior.MERGE; const resultsFromLink = () => this.getResultsFromLink(queryInfo, cacheWriteBehavior, { @@ -1667,6 +1687,7 @@ export class QueryManager { private prepareContext(context = {}) { const newContext = this.localState.prepareContext(context); return { + ...this.defaultContext, ...newContext, clientAwareness: this.clientAwareness, }; diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 3104be8ea96..98c1f735026 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -24,6 +24,7 @@ import { itAsync, MockLink, mockSingleLink, + MockSubscriptionLink, subscribeAndCount, wait, } from "../../testing"; @@ -34,6 +35,7 @@ import wrap from "../../testing/core/wrap"; import { resetStore } from "./QueryManager"; import { SubscriptionObserver } from "zen-observable-ts"; import { waitFor } from "@testing-library/react"; +import { ObservableStream } from "../../testing/internal"; export const mockFetchQuery = (queryManager: QueryManager) => { const fetchConcastWithInfo = queryManager["fetchConcastWithInfo"]; @@ -46,7 +48,8 @@ export const mockFetchQuery = (queryManager: QueryManager) => { >( original: T ) => - jest.fn, Parameters>(function () { + jest.fn, Parameters>(function (): ReturnType { + // @ts-expect-error return original.apply(queryManager, arguments); }); @@ -1085,6 +1088,98 @@ describe("ObservableQuery", () => { } ); + it("calling refetch with different variables before the query itself resolved will only yield the result for the new variables", async () => { + const observers: SubscriptionObserver>[] = []; + const queryManager = new QueryManager({ + cache: new InMemoryCache(), + link: new ApolloLink((operation, forward) => { + return new Observable((observer) => { + observers.push(observer); + }); + }), + }); + const observableQuery = queryManager.watchQuery({ + query, + variables: { id: 1 }, + }); + const stream = new ObservableStream(observableQuery); + + observableQuery.refetch({ id: 2 }); + + observers[0].next({ data: dataOne }); + observers[0].complete(); + + observers[1].next({ data: dataTwo }); + observers[1].complete(); + + { + const result = await stream.takeNext(); + expect(result).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: dataTwo, + }); + } + expect(stream.take()).rejects.toThrow(/Timeout/i); + }); + + it("calling refetch multiple times with different variables will return only results for the most recent variables", async () => { + const observers: SubscriptionObserver>[] = []; + const queryManager = new QueryManager({ + cache: new InMemoryCache(), + link: new ApolloLink((operation, forward) => { + return new Observable((observer) => { + observers.push(observer); + }); + }), + }); + const observableQuery = queryManager.watchQuery({ + query, + variables: { id: 1 }, + }); + const stream = new ObservableStream(observableQuery); + + observers[0].next({ data: dataOne }); + observers[0].complete(); + + { + const result = await stream.takeNext(); + expect(result).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: dataOne, + }); + } + + observableQuery.refetch({ id: 2 }); + observableQuery.refetch({ id: 3 }); + + observers[1].next({ data: dataTwo }); + observers[1].complete(); + + observers[2].next({ + data: { + people_one: { + name: "SomeOneElse", + }, + }, + }); + observers[2].complete(); + + { + const result = await stream.takeNext(); + expect(result).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: { + people_one: { + name: "SomeOneElse", + }, + }, + }); + } + }); + itAsync( "calls fetchRequest with fetchPolicy `no-cache` when using `no-cache` fetch policy", (resolve, reject) => { @@ -2295,6 +2390,124 @@ describe("ObservableQuery", () => { } ); + it("handles multiple calls to getCurrentResult without losing data", async () => { + const query = gql` + { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const obs = client.watchQuery({ query }); + const stream = new ObservableStream(obs); + + link.simulateResult({ + result: { + data: { + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }, + hasNext: true, + }, + }); + + { + const result = await stream.takeNext(); + expect(result.data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + } + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + + link.simulateResult( + { + result: { + incremental: [ + { + data: { + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + + { + const result = await stream.takeNext(); + expect(result.data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + } + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + }); + { type Result = Partial>; @@ -2740,7 +2953,7 @@ describe("ObservableQuery", () => { throw new Error("not reached"); } catch (error) { expect(error).toBeInstanceOf(TypeError); - expect(error.message).toMatch( + expect((error as Error).message).toMatch( /Cannot assign to read only property 'value'/ ); } diff --git a/src/core/__tests__/QueryManager/index.ts b/src/core/__tests__/QueryManager/index.ts index 77fd0fe6dbd..49068a77548 100644 --- a/src/core/__tests__/QueryManager/index.ts +++ b/src/core/__tests__/QueryManager/index.ts @@ -47,6 +47,7 @@ import observableToPromise, { import { itAsync, subscribeAndCount } from "../../../testing/core"; import { ApolloClient } from "../../../core"; import { mockFetchQuery } from "../ObservableQuery"; +import { Concast, print } from "../../../utilities"; interface MockedMutation { reject: (reason: any) => any; @@ -1610,7 +1611,7 @@ describe("QueryManager", () => { }); }); - const getIdField = ({ id }: { id: string }) => id; + const getIdField = (obj: any) => obj.id; itAsync( "runs a mutation with object parameters and puts the result in the store", @@ -5848,9 +5849,8 @@ describe("QueryManager", () => { const variables = { id: "1234" }; - const refetchError = testQueryError - ? new Error("Refetch failed") - : undefined; + const refetchError = + testQueryError ? new Error("Refetch failed") : undefined; const queryManager = mockQueryManager( { @@ -6127,7 +6127,11 @@ describe("QueryManager", () => { queryManager.query({ query, context: { queryDeduplication: true } }); - expect(queryManager["inFlightLinkObservables"].size).toBe(1); + expect( + queryManager["inFlightLinkObservables"].peek(print(query), "{}") + ).toEqual({ + observable: expect.any(Concast), + }); }); it("should allow overriding global queryDeduplication: true to false", () => { @@ -6153,7 +6157,9 @@ describe("QueryManager", () => { queryManager.query({ query, context: { queryDeduplication: false } }); - expect(queryManager["inFlightLinkObservables"].size).toBe(0); + expect( + queryManager["inFlightLinkObservables"].peek(print(query), "{}") + ).toBeUndefined(); }); }); @@ -6253,4 +6259,229 @@ 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.each([ + ["query", { method: "query", option: "query" }], + ["mutation", { method: "mutate", option: "mutation" }], + ["subscription", { method: "subscribe", option: "query" }], + ] as const)( + "`defaultContext` will be applied to the context of a %s", + async (_, { method, option }) => { + 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", + }, + }); + + // @ts-ignore a bit too generic for TS + client[method]({ + [option]: 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(); + } + ); + }); }); diff --git a/src/core/__tests__/fetchPolicies.ts b/src/core/__tests__/fetchPolicies.ts index 27c691aab06..f5ed3743f4b 100644 --- a/src/core/__tests__/fetchPolicies.ts +++ b/src/core/__tests__/fetchPolicies.ts @@ -871,9 +871,9 @@ describe("nextFetchPolicy", () => { }) => itAsync( `transitions ${args.fetchPolicy} to ${ - typeof args.nextFetchPolicy === "function" - ? args.nextFetchPolicy.name - : args.nextFetchPolicy + typeof args.nextFetchPolicy === "function" ? + args.nextFetchPolicy.name + : args.nextFetchPolicy } (${args.useDefaultOptions ? "" : "not "}using defaults)`, (resolve, reject) => { const client = new ApolloClient({ @@ -882,8 +882,9 @@ describe("nextFetchPolicy", () => { addTypename: true, }), defaultOptions: { - watchQuery: args.useDefaultOptions - ? { + watchQuery: + args.useDefaultOptions ? + { nextFetchPolicy: args.nextFetchPolicy, } : {}, @@ -1273,7 +1274,7 @@ describe("nextFetchPolicy", () => { // The nextFetchPolicy function we provided always returnes cache-first, // even when context.reason is variables-changed (which by default - // resets the fetchPolicy to context.initialPolicy), so cache-first is + // resets the fetchPolicy to context.initialFetchPolicy), so cache-first is // still what we see here. expect(observable.options.fetchPolicy).toBe("cache-first"); } else if (count === 3) { diff --git a/src/core/index.ts b/src/core/index.ts index 72340d689d7..785b2d03efc 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -14,6 +14,8 @@ export type { SubscriptionOptions, FetchPolicy, WatchQueryFetchPolicy, + MutationFetchPolicy, + RefetchWritePolicy, ErrorPolicy, FetchMoreQueryOptions, SubscribeToMoreOptions, @@ -38,6 +40,8 @@ export type { FieldMergeFunction, FieldFunctionOptions, PossibleTypesMap, + WatchFragmentOptions, + WatchFragmentResult, } from "../cache/index.js"; export { Cache, diff --git a/src/core/types.ts b/src/core/types.ts index 832f2533f57..8085d013839 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -74,24 +74,21 @@ export type RefetchQueriesPromiseResults = // we get if we don't check for any. I hoped `any extends TResult` would do // the trick here, instead of IsStrictlyAny, but you can see for yourself what // fails in the refetchQueries tests if you try making that simplification. - IsStrictlyAny extends true - ? any[] - : // If the onQueryUpdated function passed to client.refetchQueries returns true - // or false, that means either to refetch the query (true) or to skip the - // query (false). Since refetching produces an ApolloQueryResult, and - // skipping produces nothing, the fully-resolved array of all results produced - // will be an ApolloQueryResult[], when TResult extends boolean. - TResult extends boolean - ? ApolloQueryResult[] - : // If onQueryUpdated returns a PromiseLike, that thenable will be passed as - // an array element to Promise.all, so we infer/unwrap the array type U here. - TResult extends PromiseLike - ? U[] - : // All other onQueryUpdated results end up in the final Promise.all array as - // themselves, with their original TResult type. Note that TResult will - // default to ApolloQueryResult if no onQueryUpdated function is passed - // to client.refetchQueries. - TResult[]; + IsStrictlyAny extends true ? any[] + : // If the onQueryUpdated function passed to client.refetchQueries returns true + // or false, that means either to refetch the query (true) or to skip the + // query (false). Since refetching produces an ApolloQueryResult, and + // skipping produces nothing, the fully-resolved array of all results produced + // will be an ApolloQueryResult[], when TResult extends boolean. + TResult extends boolean ? ApolloQueryResult[] + : // If onQueryUpdated returns a PromiseLike, that thenable will be passed as + // an array element to Promise.all, so we infer/unwrap the array type U here. + TResult extends PromiseLike ? U[] + : // All other onQueryUpdated results end up in the final Promise.all array as + // themselves, with their original TResult type. Note that TResult will + // default to ApolloQueryResult if no onQueryUpdated function is passed + // to client.refetchQueries. + TResult[]; // The result of client.refetchQueries is thenable/awaitable, if you just want // an array of fully resolved results, but you can also access the raw results @@ -126,12 +123,11 @@ export type InternalRefetchQueriesResult = // If onQueryUpdated returns a boolean, that's equivalent to refetching the // query when the boolean is true and skipping the query when false, so the // internal type of refetched results is Promise>. - TResult extends boolean - ? Promise> - : // Otherwise, onQueryUpdated returns whatever it returns. If onQueryUpdated is - // not provided, TResult defaults to Promise> (see the - // generic type parameters of client.refetchQueries). - TResult; + TResult extends boolean ? Promise> + : // Otherwise, onQueryUpdated returns whatever it returns. If onQueryUpdated is + // not provided, TResult defaults to Promise> (see the + // generic type parameters of client.refetchQueries). + TResult; export type InternalRefetchQueriesMap = Map< ObservableQuery, @@ -143,7 +139,7 @@ export type { QueryOptions as PureQueryOptions }; export type OperationVariables = Record; -export type ApolloQueryResult = { +export interface ApolloQueryResult { data: T; /** * A list of any errors that occurred during server-side execution of a GraphQL operation. @@ -162,7 +158,7 @@ export type ApolloQueryResult = { // result.partial will be true. Otherwise, result.partial will be falsy // (usually because the property is absent from the result object). partial?: boolean; -}; +} // This is part of the public API, people write these functions in `updateQueries`. export type MutationQueryReducer = ( @@ -178,7 +174,9 @@ export type MutationQueryReducersMap = { [queryName: string]: MutationQueryReducer; }; -// @deprecated Use MutationUpdaterFunction instead. +/** + * @deprecated Use `MutationUpdaterFunction` instead. + */ export type MutationUpdaterFn = ( // The MutationUpdaterFn type is broken because it mistakenly uses the same // type parameter T for both the cache and the mutationResult. Do not use this diff --git a/src/core/watchQueryOptions.ts b/src/core/watchQueryOptions.ts index a5a9a7d9afc..5810c6464c4 100644 --- a/src/core/watchQueryOptions.ts +++ b/src/core/watchQueryOptions.ts @@ -12,6 +12,7 @@ import type { } from "./types.js"; import type { ApolloCache } from "../cache/index.js"; import type { ObservableQuery } from "./ObservableQuery.js"; +import type { IgnoreModifier } from "../cache/core/types/common.js"; /** * fetchPolicy determines where the client may return a result from. The options are: @@ -41,7 +42,7 @@ export type RefetchWritePolicy = "merge" | "overwrite"; /** * errorPolicy determines the level of events for errors in the execution result. The options are: - * - none (default): any errors from the request are treated like runtime errors and the observable is stopped (XXX this is default to lower breaking changes going from AC 1.0 => 2.0) + * - none (default): any errors from the request are treated like runtime errors and the observable is stopped * - ignore: errors from the request do not stop the observable, but also don't call `next` * - all: errors are treated like data and will notify observables */ @@ -51,64 +52,34 @@ export type ErrorPolicy = "none" | "ignore" | "all"; * Query options. */ export interface QueryOptions { - /** - * A GraphQL document that consists of a single query to be sent down to the - * server. - */ - // TODO REFACTOR: rename this to document. Didn't do it yet because it's in a - // lot of tests. + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#query:member} */ query: DocumentNode | TypedDocumentNode; - /** - * A map going from variable name to variable value, where the variables are used - * within the GraphQL query. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ variables?: TVariables; - /** - * Specifies the {@link ErrorPolicy} to be used for this query - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#errorPolicy:member} */ errorPolicy?: ErrorPolicy; - /** - * Context to be passed to link execution chain - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ context?: DefaultContext; - /** - * Specifies the {@link FetchPolicy} to be used for this query - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: FetchPolicy; - /** - * The time interval (in milliseconds) on which this query should be - * refetched from the server. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#pollInterval:member} */ pollInterval?: number; - /** - * Whether or not updates to the network status should trigger next on the observer of this query - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#notifyOnNetworkStatusChange:member} */ notifyOnNetworkStatusChange?: boolean; - /** - * Allow returning incomplete data from the cache when a larger query cannot - * be fully satisfied by the cache, instead of returning nothing. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#returnPartialData:member} */ returnPartialData?: boolean; - /** - * If `true`, perform a query `refetch` if the query result is marked as - * being partial, and the returned data is reset to an empty Object by the - * Apollo Client `QueryManager` (due to a cache miss). - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#partialRefetch:member} */ partialRefetch?: boolean; - /** - * Whether to canonize cache results before returning them. Canonization - * takes some extra time, but it speeds up future deep equality comparisons. - * Defaults to false. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ canonizeResults?: boolean; } @@ -118,15 +89,19 @@ export interface QueryOptions { export interface WatchQueryOptions< TVariables extends OperationVariables = OperationVariables, TData = any, -> extends Omit, "fetchPolicy"> { - /** - * Specifies the {@link FetchPolicy} to be used for this query. - */ +> extends SharedWatchQueryOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#query:member} */ + query: DocumentNode | TypedDocumentNode; +} + +export interface SharedWatchQueryOptions< + TVariables extends OperationVariables, + TData, +> { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: WatchQueryFetchPolicy; - /** - * Specifies the {@link FetchPolicy} to be used after this query has completed. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#nextFetchPolicy:member} */ nextFetchPolicy?: | WatchQueryFetchPolicy | (( @@ -135,20 +110,38 @@ export interface WatchQueryOptions< context: NextFetchPolicyContext ) => WatchQueryFetchPolicy); - /** - * Defaults to the initial value of options.fetchPolicy, but can be explicitly - * configured to specify the WatchQueryFetchPolicy to revert back to whenever - * variables change (unless nextFetchPolicy intervenes). - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#initialFetchPolicy:member} */ initialFetchPolicy?: WatchQueryFetchPolicy; - /** - * Specifies whether a {@link NetworkStatus.refetch} operation should merge - * incoming field data with existing data, or overwrite the existing data. - * Overwriting is probably preferable, but merging is currently the default - * behavior, for backwards compatibility with Apollo Client 3.x. - */ + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#refetchWritePolicy:member} */ refetchWritePolicy?: RefetchWritePolicy; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ + variables?: TVariables; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#errorPolicy:member} */ + errorPolicy?: ErrorPolicy; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ + context?: DefaultContext; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#pollInterval:member} */ + pollInterval?: number; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#notifyOnNetworkStatusChange:member} */ + notifyOnNetworkStatusChange?: boolean; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#returnPartialData:member} */ + returnPartialData?: boolean; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#partialRefetch:member} */ + partialRefetch?: boolean; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ + canonizeResults?: boolean; + + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#skipPollAttempt:member} */ + skipPollAttempt?: () => boolean; } export interface NextFetchPolicyContext< @@ -162,7 +155,9 @@ export interface NextFetchPolicyContext< } export interface FetchMoreQueryOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#query:member} */ query?: DocumentNode | TypedDocumentNode; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ variables?: Partial; context?: DefaultContext; } @@ -197,31 +192,19 @@ export interface SubscriptionOptions< TVariables = OperationVariables, TData = any, > { - /** - * A GraphQL document, often created with `gql` from the `graphql-tag` - * package, that contains a single subscription inside of it. - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#query:member} */ query: DocumentNode | TypedDocumentNode; - /** - * An object that maps from the name of a variable as used in the subscription - * GraphQL document to that variable's value. - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#variables:member} */ variables?: TVariables; - /** - * Specifies the {@link FetchPolicy} to be used for this subscription. - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: FetchPolicy; - /** - * Specifies the {@link ErrorPolicy} to be used for this operation - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#errorPolicy:member} */ errorPolicy?: ErrorPolicy; - /** - * Context object to be passed through the link execution chain. - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#context:member} */ context?: DefaultContext; } @@ -231,92 +214,35 @@ export interface MutationBaseOptions< TContext = DefaultContext, TCache extends ApolloCache = ApolloCache, > { - /** - * An object that represents the result of this mutation that will be - * optimistically stored before the server has actually returned a result. - * This is most often used for optimistic UI, where we want to be able to see - * the result of a mutation immediately, and update the UI later if any errors - * appear. - */ - optimisticResponse?: TData | ((vars: TVariables) => TData); - - /** - * A {@link MutationQueryReducersMap}, which is map from query names to - * mutation query reducers. Briefly, this map defines how to incorporate the - * results of the mutation into the results of queries that are currently - * being watched by your application. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#optimisticResponse:member} */ + optimisticResponse?: + | TData + | ((vars: TVariables, { IGNORE }: { IGNORE: IgnoreModifier }) => TData); + + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#updateQueries:member} */ updateQueries?: MutationQueryReducersMap; - /** - * A list of query names which will be refetched once this mutation has - * returned. This is often used if you have a set of queries which may be - * affected by a mutation and will have to update. Rather than writing a - * mutation query reducer (i.e. `updateQueries`) for this, you can simply - * refetch the queries that will be affected and achieve a consistent store - * once these queries return. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#refetchQueries:member} */ refetchQueries?: | ((result: FetchResult) => InternalRefetchQueriesInclude) | InternalRefetchQueriesInclude; - /** - * By default, `refetchQueries` does not wait for the refetched queries to - * be completed, before resolving the mutation `Promise`. This ensures that - * query refetching does not hold up mutation response handling (query - * refetching is handled asynchronously). Set `awaitRefetchQueries` to - * `true` if you would like to wait for the refetched queries to complete, - * before the mutation can be marked as resolved. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#awaitRefetchQueries:member} */ awaitRefetchQueries?: boolean; - /** - * A function which provides an {@link ApolloCache} instance, and the result - * of the mutation, to allow the user to update the store based on the - * results of the mutation. - * - * This function will be called twice over the lifecycle of a mutation. Once - * at the very beginning if an `optimisticResponse` was provided. The writes - * created from the optimistic data will be rolled back before the second time - * this function is called which is when the mutation has successfully - * resolved. At that point `update` will be called with the *actual* mutation - * result and those writes will not be rolled back. - * - * Note that since this function is intended to be used to update the - * store, it cannot be used with a `no-cache` fetch policy. If you're - * interested in performing some action after a mutation has completed, - * and you don't need to update the store, use the Promise returned from - * `client.mutate` instead. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#update:member} */ update?: MutationUpdaterFunction; - /** - * A function that will be called for each ObservableQuery affected by - * this mutation, after the mutation has completed. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#onQueryUpdated:member} */ onQueryUpdated?: OnQueryUpdated; - /** - * Specifies the {@link ErrorPolicy} to be used for this operation - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#errorPolicy:member} */ errorPolicy?: ErrorPolicy; - /** - * An object that maps from the name of a variable as used in the mutation - * GraphQL document to that variable's value. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#variables:member} */ variables?: TVariables; - /** - * The context to be passed to the link execution chain. This context will - * only be used with this mutation. It will not be used with - * `refetchQueries`. Refetched queries use the context they were - * initialized with (since the initial context is stored as part of the - * `ObservableQuery` instance). If a specific context is needed when - * refetching queries, make sure it is configured (via the - * [query `context` option](https://www.apollographql.com/docs/react/api/apollo-client#ApolloClient.query)) - * when the query is first initialized/run. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#context:member} */ context?: TContext; } @@ -325,28 +251,19 @@ export interface MutationOptions< TVariables = OperationVariables, TContext = DefaultContext, TCache extends ApolloCache = ApolloCache, -> extends MutationBaseOptions { - /** - * A GraphQL document, often created with `gql` from the `graphql-tag` - * package, that contains a single mutation inside of it. - */ +> extends MutationSharedOptions { + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#mutation:member} */ mutation: DocumentNode | TypedDocumentNode; - - /** - * Specifies the {@link MutationFetchPolicy} to be used for this query. - * Mutations support only 'network-only' and 'no-cache' fetchPolicy strings. - * If fetchPolicy is not provided, it defaults to 'network-only'. - */ +} +export interface MutationSharedOptions< + TData = any, + TVariables = OperationVariables, + TContext = DefaultContext, + TCache extends ApolloCache = ApolloCache, +> extends MutationBaseOptions { + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: MutationFetchPolicy; - /** - * To avoid retaining sensitive information from mutation root field - * arguments, Apollo Client v3.4+ automatically clears any `ROOT_MUTATION` - * fields from the cache after each mutation finishes. If you need this - * information to remain in the cache, you can prevent the removal by passing - * `keepRootFields: true` to the mutation. `ROOT_MUTATION` result data are - * also passed to the mutation `update` function, so we recommend obtaining - * the results that way, rather than using this option, if possible. - */ + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#keepRootFields:member} */ keepRootFields?: boolean; } diff --git a/src/dev/index.ts b/src/dev/index.ts index 7d6b01f873f..bf77b7e5326 100644 --- a/src/dev/index.ts +++ b/src/dev/index.ts @@ -1,3 +1,5 @@ export { loadDevMessages } from "./loadDevMessages.js"; export { loadErrorMessageHandler } from "./loadErrorMessageHandler.js"; export { loadErrorMessages } from "./loadErrorMessages.js"; +export { setErrorMessageHandler } from "./setErrorMessageHandler.js"; +export type { ErrorMessageHandler } from "./setErrorMessageHandler.js"; diff --git a/src/dev/loadErrorMessageHandler.ts b/src/dev/loadErrorMessageHandler.ts index ad18c159ed5..8f99a34ae9a 100644 --- a/src/dev/loadErrorMessageHandler.ts +++ b/src/dev/loadErrorMessageHandler.ts @@ -1,27 +1,31 @@ import type { ErrorCodes } from "../invariantErrorCodes.js"; import { global } from "../utilities/globals/index.js"; import { ApolloErrorMessageHandler } from "../utilities/globals/invariantWrappers.js"; +import type { ErrorMessageHandler } from "./setErrorMessageHandler.js"; +import { setErrorMessageHandler } from "./setErrorMessageHandler.js"; +/** + * Injects Apollo Client's default error message handler into the application and + * also loads the error codes that are passed in as arguments. + */ export function loadErrorMessageHandler(...errorCodes: ErrorCodes[]) { - if (!global[ApolloErrorMessageHandler]) { - global[ApolloErrorMessageHandler] = handler as typeof handler & ErrorCodes; - } + setErrorMessageHandler(handler as typeof handler & ErrorCodes); for (const codes of errorCodes) { - Object.assign(global[ApolloErrorMessageHandler], codes); + Object.assign(handler, codes); } - return global[ApolloErrorMessageHandler]; + return handler; +} - function handler(message: string | number, args: unknown[]) { - if (typeof message === "number") { - const definition = global[ApolloErrorMessageHandler]![message]; - if (!message || !definition?.message) return; - message = definition.message; - } - return args.reduce( - (msg, arg) => msg.replace(/%[sdfo]/, String(arg)), - String(message) - ); +const handler = ((message: string | number, args: unknown[]) => { + if (typeof message === "number") { + const definition = global[ApolloErrorMessageHandler]![message]; + if (!message || !definition?.message) return; + message = definition.message; } -} + return args.reduce( + (msg, arg) => msg.replace(/%[sdfo]/, String(arg)), + String(message) + ); +}) as ErrorMessageHandler & ErrorCodes; diff --git a/src/dev/setErrorMessageHandler.ts b/src/dev/setErrorMessageHandler.ts new file mode 100644 index 00000000000..2dc822d0485 --- /dev/null +++ b/src/dev/setErrorMessageHandler.ts @@ -0,0 +1,40 @@ +import type { ErrorCodes } from "../invariantErrorCodes.js"; +import { global } from "../utilities/globals/index.js"; +import { ApolloErrorMessageHandler } from "../utilities/globals/invariantWrappers.js"; + +/** + * The error message handler is a function that is called when a message is + * logged or an error is thrown to determine the contents of the error message + * to be logged or thrown. + */ +export type ErrorMessageHandler = { + /** + * @param message - Usually the error message number (as defined in + * `@apollo/client/invariantErrorCodes.js`). + * In some edge cases, this can already be a string, that can be passed through + * as an error message. + * + * @param args - The placeholders that can be passed into the error message (pre-stringified). + * These relate with the `%s` and `%d` [substitution strings](https://developer.mozilla.org/en-US/docs/Web/API/console#using_string_substitutions) + * in the error message defined in `@apollo/client/invariantErrorCodes.js`. + * + * ⚠️ Note that arguments will only be passed in for error messages. + * For normal log messages, you will get an empty array here and they will directly + * be passed to `console.log` instead, to have the string subsitution done by the + * engine, as that allows for nicer (and in the case of a browser, interactive) + * output. + * + * @returns The error message to be logged or thrown. If it returns `undefined`, + * the mechanism will fall back to the default: + * A link to https://go.apollo.dev/c/err with Apollo Client version, + * the error message number, and the error message arguments encoded into + * the URL hash. + */ (message: string | number, args: string[]): string | undefined; +}; + +/** + * Overrides the global "Error Message Handler" with a custom implementation. + */ +export function setErrorMessageHandler(handler: ErrorMessageHandler) { + global[ApolloErrorMessageHandler] = handler as typeof handler & ErrorCodes; +} diff --git a/src/link/batch-http/__tests__/batchHttpLink.ts b/src/link/batch-http/__tests__/batchHttpLink.ts index 525636addb6..1209b0414c1 100644 --- a/src/link/batch-http/__tests__/batchHttpLink.ts +++ b/src/link/batch-http/__tests__/batchHttpLink.ts @@ -6,6 +6,7 @@ import { ApolloLink } from "../../core/ApolloLink"; import { execute } from "../../core/execute"; import { Observable, + ObservableSubscription, Observer, } from "../../../utilities/observables/Observable"; import { BatchHttpLink } from "../batchHttpLink"; @@ -35,10 +36,11 @@ function makeCallback( ) { return function () { try { + // @ts-expect-error callback.apply(this, arguments); resolve(); } catch (error) { - reject(error); + reject(error as Error); } } as typeof callback; } @@ -270,6 +272,8 @@ const createHttpLink = (httpArgs?: any) => { return new BatchHttpLink(args); }; +const subscriptions = new Set(); + describe("SharedHttpTest", () => { const data = { data: { hello: "world" } }; const data2 = { data: { hello: "everyone" } }; @@ -299,10 +303,16 @@ describe("SharedHttpTest", () => { error, complete, }; + subscriptions.clear(); }); afterEach(() => { fetchMock.restore(); + if (subscriptions.size) { + // Tests within this describe block can add subscriptions to this Set + // that they want to be canceled/unsubscribed after the test finishes. + subscriptions.forEach((sub) => sub.unsubscribe()); + } }); it("raises warning if called with concat", () => { @@ -472,7 +482,7 @@ describe("SharedHttpTest", () => { after(); } catch (e) { - reject(e); + reject(e as Error); } }, }); @@ -523,6 +533,9 @@ describe("SharedHttpTest", () => { expect(subscriber.next).toHaveBeenCalledTimes(2); expect(subscriber.complete).toHaveBeenCalledTimes(2); expect(subscriber.error).not.toHaveBeenCalled(); + // only one call because batchHttpLink can handle more than one subscriber + // without starting a new request + expect(fetchMock.calls().length).toBe(1); resolve(); }, 50); }); @@ -620,6 +633,61 @@ describe("SharedHttpTest", () => { ); }); + it("uses the latest window.fetch function if options.fetch not configured", (done) => { + const httpLink = createHttpLink({ uri: "data" }); + + const fetch = window.fetch; + expect(typeof fetch).toBe("function"); + + const fetchSpy = jest.spyOn(window, "fetch"); + fetchSpy.mockImplementation(() => + Promise.resolve({ + text() { + return Promise.resolve( + JSON.stringify({ + data: { hello: "from spy" }, + }) + ); + }, + } as Response) + ); + + const spyFn = window.fetch; + expect(spyFn).not.toBe(fetch); + + subscriptions.add( + execute(httpLink, { + query: sampleQuery, + }).subscribe({ + error: done.fail, + + next(result) { + expect(fetchSpy).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + data: { hello: "from spy" }, + }); + + fetchSpy.mockRestore(); + expect(window.fetch).toBe(fetch); + + subscriptions.add( + execute(httpLink, { + query: sampleQuery, + }).subscribe({ + error: done.fail, + next(result) { + expect(result).toEqual({ + data: { hello: "world" }, + }); + done(); + }, + }) + ); + }, + }) + ); + }); + itAsync( "prioritizes context headers over setup headers", (resolve, reject) => { diff --git a/src/link/batch-http/batchHttpLink.ts b/src/link/batch-http/batchHttpLink.ts index cb80e8d3809..d0412f68cf2 100644 --- a/src/link/batch-http/batchHttpLink.ts +++ b/src/link/batch-http/batchHttpLink.ts @@ -3,6 +3,7 @@ import { ApolloLink } from "../core/index.js"; import { Observable, hasDirectives, + maybe, removeClientSetsFromDocument, } from "../../utilities/index.js"; import { fromError } from "../utils/index.js"; @@ -27,6 +28,8 @@ export namespace BatchHttpLink { Omit; } +const backupFetch = maybe(() => fetch); + /** * Transforms Operation for into HTTP results. * context can include the headers property, which will be passed to the fetch function @@ -43,7 +46,7 @@ export class BatchHttpLink extends ApolloLink { let { uri = "/graphql", // use default global fetch if nothing is passed in - fetch: fetcher, + fetch: preferredFetch, print = defaultPrinter, includeExtensions, preserveHeaderCase, @@ -55,14 +58,10 @@ export class BatchHttpLink extends ApolloLink { ...requestOptions } = fetchParams || ({} as BatchHttpLink.Options); - // dev warnings to ensure fetch is present - checkFetcher(fetcher); - - //fetcher is set here rather than the destructuring to ensure fetch is - //declared before referencing it. Reference in the destructuring would cause - //a ReferenceError - if (!fetcher) { - fetcher = fetch; + if (__DEV__) { + // Make sure at least one of preferredFetch, window.fetch, or backupFetch + // is defined, so requests won't fail at runtime. + checkFetcher(preferredFetch || backupFetch); } const linkConfig = { @@ -163,7 +162,16 @@ export class BatchHttpLink extends ApolloLink { } return new Observable((observer) => { - fetcher!(chosenURI, options) + // Prefer BatchHttpLink.Options.fetch (preferredFetch) if provided, and + // otherwise fall back to the *current* global window.fetch function + // (see issue #7832), or (if all else fails) the backupFetch function we + // saved when this module was first evaluated. This last option protects + // against the removal of window.fetch, which is unlikely but not + // impossible. + const currentFetch = + preferredFetch || maybe(() => fetch) || backupFetch; + + currentFetch!(chosenURI, options) .then((response) => { // Make the raw response available in the context. operations.forEach((operation) => diff --git a/src/link/batch/__tests__/batchLink.ts b/src/link/batch/__tests__/batchLink.ts index 1a564fd2796..e5930924c27 100644 --- a/src/link/batch/__tests__/batchLink.ts +++ b/src/link/batch/__tests__/batchLink.ts @@ -64,6 +64,7 @@ function terminatingCheck( ) { return function () { try { + // @ts-expect-error callback.apply(this, arguments); resolve(); } catch (error) { diff --git a/src/link/core/ApolloLink.ts b/src/link/core/ApolloLink.ts index c59219727ba..4f7759915d6 100644 --- a/src/link/core/ApolloLink.ts +++ b/src/link/core/ApolloLink.ts @@ -45,19 +45,21 @@ export class ApolloLink { const leftLink = toLink(left); const rightLink = toLink(right || new ApolloLink(passthrough)); + let ret: ApolloLink; if (isTerminating(leftLink) && isTerminating(rightLink)) { - return new ApolloLink((operation) => { - return test(operation) - ? leftLink.request(operation) || Observable.of() + ret = new ApolloLink((operation) => { + return test(operation) ? + leftLink.request(operation) || Observable.of() : rightLink.request(operation) || Observable.of(); }); } else { - return new ApolloLink((operation, forward) => { - return test(operation) - ? leftLink.request(operation, forward) || Observable.of() + ret = new ApolloLink((operation, forward) => { + return test(operation) ? + leftLink.request(operation, forward) || Observable.of() : rightLink.request(operation, forward) || Observable.of(); }); } + return Object.assign(ret, { left: leftLink, right: rightLink }); } public static execute( @@ -88,8 +90,9 @@ export class ApolloLink { } const nextLink = toLink(second); + let ret: ApolloLink; if (isTerminating(nextLink)) { - return new ApolloLink( + ret = new ApolloLink( (operation) => firstLink.request( operation, @@ -97,7 +100,7 @@ export class ApolloLink { ) || Observable.of() ); } else { - return new ApolloLink((operation, forward) => { + ret = new ApolloLink((operation, forward) => { return ( firstLink.request(operation, (op) => { return nextLink.request(op, forward) || Observable.of(); @@ -105,6 +108,7 @@ export class ApolloLink { ); }); } + return Object.assign(ret, { left: firstLink, right: nextLink }); } constructor(request?: RequestHandler) { @@ -154,4 +158,21 @@ export class ApolloLink { this.onError = fn; return this; } + + /** + * @internal + * Used to iterate through all links that are concatenations or `split` links. + */ + readonly left?: ApolloLink; + /** + * @internal + * Used to iterate through all links that are concatenations or `split` links. + */ + readonly right?: ApolloLink; + + /** + * @internal + * Can be provided by a link that has an internal cache to report it's memory details. + */ + getMemoryInternals?: () => unknown; } diff --git a/src/link/core/types.ts b/src/link/core/types.ts index 0e0e815e192..a898f1a598e 100644 --- a/src/link/core/types.ts +++ b/src/link/core/types.ts @@ -76,7 +76,14 @@ export interface Operation { variables: Record; operationName: string; extensions: Record; - setContext: (context: DefaultContext) => DefaultContext; + setContext: { + (context: Partial): void; + ( + updateContext: ( + previousContext: DefaultContext + ) => Partial + ): void; + }; getContext: () => DefaultContext; } @@ -85,6 +92,7 @@ export interface SingleExecutionResult< TContext = DefaultContext, TExtensions = Record, > extends ExecutionResult { + // data might be undefined if errorPolicy was set to 'ignore' data?: TData | null; context?: TContext; } diff --git a/src/link/error/index.ts b/src/link/error/index.ts index 039b6e193e2..00b0701ab6f 100644 --- a/src/link/error/index.ts +++ b/src/link/error/index.ts @@ -85,7 +85,7 @@ export function onError(errorHandler: ErrorHandler): ApolloLink { }, }); } catch (e) { - errorHandler({ networkError: e, operation, forward }); + errorHandler({ networkError: e as Error, operation, forward }); observer.error(e); } diff --git a/src/link/http/HttpLink.ts b/src/link/http/HttpLink.ts index 06479e050f2..c59c04566cf 100644 --- a/src/link/http/HttpLink.ts +++ b/src/link/http/HttpLink.ts @@ -1,10 +1,8 @@ -import type { RequestHandler } from "../core/index.js"; import { ApolloLink } from "../core/index.js"; import type { HttpOptions } from "./selectHttpOptionsAndBody.js"; import { createHttpLink } from "./createHttpLink.js"; export class HttpLink extends ApolloLink { - public requester: RequestHandler; constructor(public options: HttpOptions = {}) { super(createHttpLink(options).request); } diff --git a/src/link/http/__tests__/HttpLink.ts b/src/link/http/__tests__/HttpLink.ts index 4523c4d0fcb..ad58e4c40c9 100644 --- a/src/link/http/__tests__/HttpLink.ts +++ b/src/link/http/__tests__/HttpLink.ts @@ -2,7 +2,7 @@ import gql from "graphql-tag"; import fetchMock from "fetch-mock"; import { ASTNode, print, stripIgnoredCharacters } from "graphql"; import { TextDecoder } from "util"; -import { ReadableStream } from "web-streams-polyfill/ponyfill/es2018"; +import { ReadableStream } from "web-streams-polyfill"; import { Readable } from "stream"; import { @@ -92,10 +92,11 @@ function makeCallback( ) { return function () { try { + // @ts-expect-error callback.apply(this, arguments); resolve(); } catch (error) { - reject(error); + reject(error as Error); } } as typeof callback; } @@ -633,6 +634,7 @@ describe("HttpLink", () => { expect(subscriber.next).toHaveBeenCalledTimes(2); expect(subscriber.complete).toHaveBeenCalledTimes(2); expect(subscriber.error).not.toHaveBeenCalled(); + expect(fetchMock.calls().length).toBe(2); resolve(); }, 50); }); @@ -886,10 +888,7 @@ describe("HttpLink", () => { itAsync("allows uri to be a function", (resolve, reject) => { const variables = { params: "stub" }; - const customFetch: WindowOrWorkerGlobalScope["fetch"] = ( - uri, - options - ) => { + const customFetch: typeof fetch = (uri, options) => { const { operationName } = convertBatchedBody(options!.body); try { expect(operationName).toBe("SampleQuery"); @@ -1202,7 +1201,7 @@ describe("HttpLink", () => { reject("warning wasn't called"); } catch (e) { makeCallback(resolve, reject, () => - expect(e.message).toMatch(/has not been found globally/) + expect((e as Error).message).toMatch(/has not been found globally/) )(); } }); @@ -1214,7 +1213,7 @@ describe("HttpLink", () => { reject("warning wasn't called"); } catch (e) { makeCallback(resolve, reject, () => - expect(e.message).toMatch(/has not been found globally/) + expect((e as Error).message).toMatch(/has not been found globally/) )(); } }); @@ -1941,6 +1940,10 @@ describe("HttpLink", () => { "Content-Type: application/json", "", '{"payload":{"data":{"aNewDieWasCreated":{"die":{"color":"blue","roll":2,"sides":5}}}}}', + "---", + "Content-Type: application/json", + "", + '{"payload": null}', "-----", ].join("\r\n"); diff --git a/src/link/http/__tests__/responseIterator.ts b/src/link/http/__tests__/responseIterator.ts index 74ffa328c60..dca254c9821 100644 --- a/src/link/http/__tests__/responseIterator.ts +++ b/src/link/http/__tests__/responseIterator.ts @@ -5,7 +5,7 @@ import { itAsync, subscribeAndCount } from "../../../testing"; import type { Observable } from "zen-observable-ts"; import { ObservableQuery } from "../../../core"; import { TextEncoder, TextDecoder } from "util"; -import { ReadableStream } from "web-streams-polyfill/ponyfill/es2018"; +import { ReadableStream } from "web-streams-polyfill"; import { Readable } from "stream"; var Blob = require("blob-polyfill").Blob; @@ -17,10 +17,11 @@ function makeCallback( ) { return function () { try { + // @ts-expect-error callback.apply(this, arguments); resolve(); } catch (error) { - reject(error); + reject(error as Error); } } as typeof callback; } diff --git a/src/link/http/__tests__/responseIteratorNoAsyncIterator.ts b/src/link/http/__tests__/responseIteratorNoAsyncIterator.ts index ec5ebe82f38..312823cf099 100644 --- a/src/link/http/__tests__/responseIteratorNoAsyncIterator.ts +++ b/src/link/http/__tests__/responseIteratorNoAsyncIterator.ts @@ -4,7 +4,7 @@ import { HttpLink } from "../HttpLink"; import { itAsync, subscribeAndCount } from "../../../testing"; import type { Observable } from "zen-observable-ts"; import { TextEncoder, TextDecoder } from "util"; -import { ReadableStream } from "web-streams-polyfill/ponyfill/es2018"; +import { ReadableStream } from "web-streams-polyfill"; import { Readable } from "stream"; // As of Jest 26 there is no way to mock/unmock a module that is used indirectly diff --git a/src/link/http/checkFetcher.ts b/src/link/http/checkFetcher.ts index 964bdd433d4..49a75586a70 100644 --- a/src/link/http/checkFetcher.ts +++ b/src/link/http/checkFetcher.ts @@ -1,8 +1,6 @@ import { newInvariantError } from "../../utilities/globals/index.js"; -export const checkFetcher = ( - fetcher: WindowOrWorkerGlobalScope["fetch"] | undefined -) => { +export const checkFetcher = (fetcher: typeof fetch | undefined) => { if (!fetcher && typeof fetch === "undefined") { throw newInvariantError(` "fetch" has not been found globally and no fetcher has been \ diff --git a/src/link/http/iterators/reader.ts b/src/link/http/iterators/reader.ts index 3a81e37cf2a..d9feac48d07 100644 --- a/src/link/http/iterators/reader.ts +++ b/src/link/http/iterators/reader.ts @@ -6,7 +6,7 @@ import { canUseAsyncIteratorSymbol } from "../../../utilities/index.js"; interface ReaderIterator { - next(): Promise>; + next(): Promise>; [Symbol.asyncIterator]?(): AsyncIterator; } @@ -15,12 +15,20 @@ export default function readerIterator( ): AsyncIterableIterator { const iterator: ReaderIterator = { next() { - return reader.read(); + return reader.read() as Promise< + | ReadableStreamReadValueResult + // DoneResult has `value` optional, which doesn't comply with an + // `IteratorResult`, so we assert it to `T | undefined` instead + | Required> + >; }, }; if (canUseAsyncIteratorSymbol) { - iterator[Symbol.asyncIterator] = function (): AsyncIterator { + iterator[Symbol.asyncIterator] = function (): AsyncIterator< + T, + T | undefined + > { return this; }; } diff --git a/src/link/http/parseAndCheckHttpResponse.ts b/src/link/http/parseAndCheckHttpResponse.ts index feb8a88d29d..32ed53d80ef 100644 --- a/src/link/http/parseAndCheckHttpResponse.ts +++ b/src/link/http/parseAndCheckHttpResponse.ts @@ -29,8 +29,9 @@ export async function readMultipartBody< // https://www.rfc-editor.org/rfc/rfc9110.html#name-parameters // e.g. multipart/mixed;boundary="graphql";deferSpec=20220824 // if no boundary is specified, default to - - const boundaryVal = contentType?.includes(delimiter) - ? contentType + const boundaryVal = + contentType?.includes(delimiter) ? + contentType ?.substring(contentType?.indexOf(delimiter) + delimiter.length) .replace(/['"]/g, "") .replace(/\;(.*)/gm, "") @@ -83,6 +84,9 @@ export async function readMultipartBody< if (isApolloPayloadResult(result)) { let next = {}; if ("payload" in result) { + if (Object.keys(result).length === 1 && result.payload === null) { + return; + } next = { ...result.payload }; } if ("errors" in result) { @@ -204,14 +208,6 @@ export function parseAndCheckHttpResponse(operations: Operation | Operation[]) { .text() .then((bodyText) => parseJsonBody(response, bodyText)) .then((result: any) => { - if (response.status >= 300) { - // Network error - throwServerError( - response, - result, - `Response not successful: Received status code ${response.status}` - ); - } if ( !Array.isArray(result) && !hasOwnProperty.call(result, "data") && @@ -222,9 +218,9 @@ export function parseAndCheckHttpResponse(operations: Operation | Operation[]) { response, result, `Server response was missing for query '${ - Array.isArray(operations) - ? operations.map((op) => op.operationName) - : operations.operationName + Array.isArray(operations) ? + operations.map((op) => op.operationName) + : operations.operationName }'.` ); } diff --git a/src/link/http/selectHttpOptionsAndBody.ts b/src/link/http/selectHttpOptionsAndBody.ts index 8789d16f302..33ec947cb37 100644 --- a/src/link/http/selectHttpOptionsAndBody.ts +++ b/src/link/http/selectHttpOptionsAndBody.ts @@ -36,7 +36,7 @@ export interface HttpOptions { /** * A `fetch`-compatible API to use when making requests. */ - fetch?: WindowOrWorkerGlobalScope["fetch"]; + fetch?: typeof fetch; /** * An object representing values to be sent as headers on the request. diff --git a/src/link/http/serializeFetchParameter.ts b/src/link/http/serializeFetchParameter.ts index 4ad2b3db2cd..5a08fe67269 100644 --- a/src/link/http/serializeFetchParameter.ts +++ b/src/link/http/serializeFetchParameter.ts @@ -9,7 +9,7 @@ export const serializeFetchParameter = (p: any, label: string) => { let serialized; try { serialized = JSON.stringify(p); - } catch (e) { + } catch (e: any) { const parseError = newInvariantError( `Network request failed. %s is not serializable: %s`, label, diff --git a/src/link/persisted-queries/__tests__/persisted-queries.test.ts b/src/link/persisted-queries/__tests__/persisted-queries.test.ts index 37fc4c9c476..7b4fecaf99f 100644 --- a/src/link/persisted-queries/__tests__/persisted-queries.test.ts +++ b/src/link/persisted-queries/__tests__/persisted-queries.test.ts @@ -66,7 +66,7 @@ const giveUpResponse = JSON.stringify({ errors: giveUpErrors }); const giveUpResponseWithCode = JSON.stringify({ errors: giveUpErrorsWithCode }); const multiResponse = JSON.stringify({ errors: multipleErrors }); -export function sha256(data: string) { +function sha256(data: string) { const hash = crypto.createHash("sha256"); hash.update(data); return hash.digest("hex"); @@ -151,6 +151,32 @@ describe("happy path", () => { }, reject); }); + it("clears the cache when calling `resetHashCache`", async () => { + fetchMock.post( + "/graphql", + () => new Promise((resolve) => resolve({ body: response })), + { repeat: 1 } + ); + + const hashRefs: WeakRef[] = []; + function hash(query: string) { + const newHash = new String(query); + hashRefs.push(new WeakRef(newHash)); + return newHash as string; + } + const persistedLink = createPersistedQuery({ sha256: hash }); + await new Promise((complete) => + execute(persistedLink.concat(createHttpLink()), { + query, + variables, + }).subscribe({ complete }) + ); + + await expect(hashRefs[0]).not.toBeGarbageCollected(); + persistedLink.resetHashCache(); + await expect(hashRefs[0]).toBeGarbageCollected(); + }); + itAsync("supports loading the hash from other method", (resolve, reject) => { fetchMock.post( "/graphql", @@ -219,7 +245,7 @@ describe("happy path", () => { reject("should have thrown an error"); } catch (error) { expect( - error.message.indexOf( + (error as Error).message.indexOf( 'Missing/invalid "sha256" or "generateHash" function' ) ).toBe(0); @@ -238,7 +264,7 @@ describe("happy path", () => { reject("should have thrown an error"); } catch (error) { expect( - error.message.indexOf( + (error as Error).message.indexOf( 'Missing/invalid "sha256" or "generateHash" function' ) ).toBe(0); @@ -517,6 +543,46 @@ describe("failure path", () => { }) ); + it.each([ + // TODO(fixme): test flake on CI https://github.com/apollographql/apollo-client/issues/11782 + // ["error message", giveUpResponse], + ["error code", giveUpResponseWithCode], + ] as const)( + "clears the cache when receiving NotSupported error (%s)", + async (_description, failingResponse) => { + fetchMock.post( + "/graphql", + () => new Promise((resolve) => resolve({ body: failingResponse })), + { repeat: 1 } + ); + fetchMock.post( + "/graphql", + () => new Promise((resolve) => resolve({ body: response })), + { repeat: 1 } + ); + + const hashRefs: WeakRef[] = []; + function hash(query: string) { + const newHash = new String(query); + hashRefs.push(new WeakRef(newHash)); + return newHash as string; + } + const persistedLink = createPersistedQuery({ sha256: hash }); + await new Promise((complete) => + execute(persistedLink.concat(createHttpLink()), { + query, + variables, + }).subscribe({ complete }) + ); + // fetch-mock holds a history of all options it has been called with + // that includes the `signal` option, which (with the native `AbortController`) + // has a reference to the `Request` instance, which will somehow reference our + // hash object + fetchMock.resetHistory(); + await expect(hashRefs[0]).toBeGarbageCollected(); + } + ); + itAsync("works with multiple errors", (resolve, reject) => { fetchMock.post( "/graphql", @@ -569,6 +635,7 @@ describe("failure path", () => { status, }); } + // @ts-expect-error return global.fetch.apply(null, args); }; const link = createPersistedQuery({ sha256 }).concat( @@ -623,6 +690,7 @@ describe("failure path", () => { status, }); } + // @ts-expect-error return global.fetch.apply(null, args); }; const link = createPersistedQuery({ sha256 }).concat( @@ -662,6 +730,7 @@ describe("failure path", () => { status, }); } + // @ts-expect-error return global.fetch.apply(null, args); }; diff --git a/src/link/persisted-queries/__tests__/react.test.tsx b/src/link/persisted-queries/__tests__/react.test.tsx index 07c3fe7375e..b05e7d98f32 100644 --- a/src/link/persisted-queries/__tests__/react.test.tsx +++ b/src/link/persisted-queries/__tests__/react.test.tsx @@ -4,6 +4,7 @@ import * as ReactDOM from "react-dom/server"; import gql from "graphql-tag"; import { print } from "graphql"; import fetchMock from "fetch-mock"; +import crypto from "crypto"; import { ApolloProvider } from "../../../react/context"; import { InMemoryCache as Cache } from "../../../cache/inmemory/inMemoryCache"; @@ -12,7 +13,12 @@ import { createHttpLink } from "../../http/createHttpLink"; import { graphql } from "../../../react/hoc/graphql"; import { getDataFromTree } from "../../../react/ssr/getDataFromTree"; import { createPersistedQueryLink as createPersistedQuery, VERSION } from ".."; -import { sha256 } from "./persisted-queries.test"; + +function sha256(data: string) { + const hash = crypto.createHash("sha256"); + hash.update(data); + return hash.digest("hex"); +} // Necessary configuration in order to mock multiple requests // to a single (/graphql) endpoint diff --git a/src/link/persisted-queries/index.ts b/src/link/persisted-queries/index.ts index b2a8c97fbce..920633bd7f3 100644 --- a/src/link/persisted-queries/index.ts +++ b/src/link/persisted-queries/index.ts @@ -12,6 +12,11 @@ import type { import { Observable, compact, isNonEmptyArray } from "../../utilities/index.js"; import type { NetworkError } from "../../errors/index.js"; import type { ServerError } from "../utils/index.js"; +import { + cacheSizes, + AutoCleanedWeakCache, + defaultCacheSizes, +} from "../../utilities/index.js"; export const VERSION = 1; @@ -93,7 +98,12 @@ function operationDefinesMutation(operation: Operation) { export const createPersistedQueryLink = ( options: PersistedQueryLink.Options ) => { - const hashesByQuery = new WeakMap>(); + let hashesByQuery: + | AutoCleanedWeakCache> + | undefined; + function resetHashCache() { + hashesByQuery = undefined; + } // Ensure a SHA-256 hash function is provided, if a custom hash // generation function is not provided. We don't supply a SHA-256 hash // function by default, to avoid forcing one as a dependency. Developers @@ -135,150 +145,175 @@ export const createPersistedQueryLink = ( // what to do with the bogus query. return getHashPromise(query); } + if (!hashesByQuery) { + hashesByQuery = new AutoCleanedWeakCache( + cacheSizes["PersistedQueryLink.persistedQueryHashes"] || + defaultCacheSizes["PersistedQueryLink.persistedQueryHashes"] + ); + } let hash = hashesByQuery.get(query)!; if (!hash) hashesByQuery.set(query, (hash = getHashPromise(query))); return hash; } - return new ApolloLink((operation, forward) => { - invariant( - forward, - "PersistedQueryLink cannot be the last link in the chain." - ); - - const { query } = operation; - - return new Observable((observer: Observer) => { - let subscription: ObservableSubscription; - let retried = false; - let originalFetchOptions: any; - let setFetchOptions = false; - const maybeRetry = ( - { - response, - networkError, - }: { response?: ExecutionResult; networkError?: ServerError }, - cb: () => void - ) => { - if (!retried && ((response && response.errors) || networkError)) { - retried = true; - - const graphQLErrors: GraphQLError[] = []; - - const responseErrors = response && response.errors; - if (isNonEmptyArray(responseErrors)) { - graphQLErrors.push(...responseErrors); - } - - // Network errors can return GraphQL errors on for example a 403 - let networkErrors; - if (typeof networkError?.result !== "string") { - networkErrors = - networkError && - networkError.result && - (networkError.result.errors as GraphQLError[]); - } - if (isNonEmptyArray(networkErrors)) { - graphQLErrors.push(...networkErrors); - } - - const disablePayload: ErrorResponse = { + return Object.assign( + new ApolloLink((operation, forward) => { + invariant( + forward, + "PersistedQueryLink cannot be the last link in the chain." + ); + + const { query } = operation; + + return new Observable((observer: Observer) => { + let subscription: ObservableSubscription; + let retried = false; + let originalFetchOptions: any; + let setFetchOptions = false; + const maybeRetry = ( + { response, networkError, - operation, - graphQLErrors: isNonEmptyArray(graphQLErrors) - ? graphQLErrors - : void 0, - meta: processErrors(graphQLErrors), - }; + }: { response?: ExecutionResult; networkError?: ServerError }, + cb: () => void + ) => { + if (!retried && ((response && response.errors) || networkError)) { + retried = true; - // if the server doesn't support persisted queries, don't try anymore - supportsPersistedQueries = !disable(disablePayload); - - // if its not found, we can try it again, otherwise just report the error - if (retry(disablePayload)) { - // need to recall the link chain - if (subscription) subscription.unsubscribe(); - // actually send the query this time - operation.setContext({ - http: { - includeQuery: true, - includeExtensions: supportsPersistedQueries, - }, - fetchOptions: { - // Since we're including the full query, which may be - // large, we should send it in the body of a POST request. - // See issue #7456. - method: "POST", - }, - }); - if (setFetchOptions) { - operation.setContext({ fetchOptions: originalFetchOptions }); + const graphQLErrors: GraphQLError[] = []; + + const responseErrors = response && response.errors; + if (isNonEmptyArray(responseErrors)) { + graphQLErrors.push(...responseErrors); } - subscription = forward(operation).subscribe(handler); - return; - } - } - cb(); - }; - const handler = { - next: (response: ExecutionResult) => { - maybeRetry({ response }, () => observer.next!(response)); - }, - error: (networkError: ServerError) => { - maybeRetry({ networkError }, () => observer.error!(networkError)); - }, - complete: observer.complete!.bind(observer), - }; - - // don't send the query the first time - operation.setContext({ - http: { - includeQuery: !supportsPersistedQueries, - includeExtensions: supportsPersistedQueries, - }, - }); + // Network errors can return GraphQL errors on for example a 403 + let networkErrors; + if (typeof networkError?.result !== "string") { + networkErrors = + networkError && + networkError.result && + (networkError.result.errors as GraphQLError[]); + } + if (isNonEmptyArray(networkErrors)) { + graphQLErrors.push(...networkErrors); + } - // If requested, set method to GET if there are no mutations. Remember the - // original fetchOptions so we can restore them if we fall back to a - // non-hashed request. - if ( - useGETForHashedQueries && - supportsPersistedQueries && - !operationDefinesMutation(operation) - ) { - operation.setContext( - ({ fetchOptions = {} }: { fetchOptions: Record }) => { - originalFetchOptions = fetchOptions; - return { - fetchOptions: { - ...fetchOptions, - method: "GET", - }, + const disablePayload: ErrorResponse = { + response, + networkError, + operation, + graphQLErrors: + isNonEmptyArray(graphQLErrors) ? graphQLErrors : void 0, + meta: processErrors(graphQLErrors), }; + + // if the server doesn't support persisted queries, don't try anymore + supportsPersistedQueries = !disable(disablePayload); + if (!supportsPersistedQueries) { + // clear hashes from cache, we don't need them anymore + resetHashCache(); + } + + // if its not found, we can try it again, otherwise just report the error + if (retry(disablePayload)) { + // need to recall the link chain + if (subscription) subscription.unsubscribe(); + // actually send the query this time + operation.setContext({ + http: { + includeQuery: true, + includeExtensions: supportsPersistedQueries, + }, + fetchOptions: { + // Since we're including the full query, which may be + // large, we should send it in the body of a POST request. + // See issue #7456. + method: "POST", + }, + }); + if (setFetchOptions) { + operation.setContext({ fetchOptions: originalFetchOptions }); + } + subscription = forward(operation).subscribe(handler); + + return; + } } - ); - setFetchOptions = true; - } + cb(); + }; + const handler = { + next: (response: ExecutionResult) => { + maybeRetry({ response }, () => observer.next!(response)); + }, + error: (networkError: ServerError) => { + maybeRetry({ networkError }, () => observer.error!(networkError)); + }, + complete: observer.complete!.bind(observer), + }; + + // don't send the query the first time + operation.setContext({ + http: { + includeQuery: !supportsPersistedQueries, + includeExtensions: supportsPersistedQueries, + }, + }); + + // If requested, set method to GET if there are no mutations. Remember the + // original fetchOptions so we can restore them if we fall back to a + // non-hashed request. + if ( + useGETForHashedQueries && + supportsPersistedQueries && + !operationDefinesMutation(operation) + ) { + operation.setContext( + ({ fetchOptions = {} }: { fetchOptions: Record }) => { + originalFetchOptions = fetchOptions; + return { + fetchOptions: { + ...fetchOptions, + method: "GET", + }, + }; + } + ); + setFetchOptions = true; + } - if (supportsPersistedQueries) { - getQueryHash(query) - .then((sha256Hash) => { - operation.extensions.persistedQuery = { - version: VERSION, - sha256Hash, - }; - subscription = forward(operation).subscribe(handler); - }) - .catch(observer.error!.bind(observer)); - } else { - subscription = forward(operation).subscribe(handler); - } + if (supportsPersistedQueries) { + getQueryHash(query) + .then((sha256Hash) => { + operation.extensions.persistedQuery = { + version: VERSION, + sha256Hash, + }; + subscription = forward(operation).subscribe(handler); + }) + .catch(observer.error!.bind(observer)); + } else { + subscription = forward(operation).subscribe(handler); + } - return () => { - if (subscription) subscription.unsubscribe(); - }; - }); - }); + return () => { + if (subscription) subscription.unsubscribe(); + }; + }); + }), + { + resetHashCache, + }, + __DEV__ ? + { + getMemoryInternals() { + return { + PersistedQueryLink: { + persistedQueryHashes: hashesByQuery?.size ?? 0, + }, + }; + }, + } + : {} + ); }; diff --git a/src/link/remove-typename/removeTypenameFromVariables.ts b/src/link/remove-typename/removeTypenameFromVariables.ts index f8362dddbc3..31ed9c58a9c 100644 --- a/src/link/remove-typename/removeTypenameFromVariables.ts +++ b/src/link/remove-typename/removeTypenameFromVariables.ts @@ -2,8 +2,14 @@ import { wrap } from "optimism"; import type { DocumentNode, TypeNode } from "graphql"; import { Kind, visit } from "graphql"; import { ApolloLink } from "../core/index.js"; -import { stripTypename, isPlainObject } from "../../utilities/index.js"; +import { + stripTypename, + isPlainObject, + cacheSizes, + defaultCacheSizes, +} from "../../utilities/index.js"; import type { OperationVariables } from "../../core/index.js"; +import { WeakCache } from "@wry/caches"; export const KEEP = "__KEEP"; @@ -18,18 +24,32 @@ export interface RemoveTypenameFromVariablesOptions { export function removeTypenameFromVariables( options: RemoveTypenameFromVariablesOptions = Object.create(null) ) { - return new ApolloLink((operation, forward) => { - const { except } = options; - const { query, variables } = operation; - - if (variables) { - operation.variables = except - ? maybeStripTypenameUsingConfig(query, variables, except) - : stripTypename(variables); - } - - return forward(operation); - }); + return Object.assign( + new ApolloLink((operation, forward) => { + const { except } = options; + const { query, variables } = operation; + + if (variables) { + operation.variables = + except ? + maybeStripTypenameUsingConfig(query, variables, except) + : stripTypename(variables); + } + + return forward(operation); + }), + __DEV__ ? + { + getMemoryInternals() { + return { + removeTypenameFromVariables: { + getVariableDefinitions: getVariableDefinitions?.size ?? 0, + }, + }; + }, + } + : {} + ); } function maybeStripTypenameUsingConfig( @@ -45,8 +65,9 @@ function maybeStripTypenameUsingConfig( const typename = variableDefinitions[key]; const typenameConfig = config[typename]; - keyVal[1] = typenameConfig - ? maybeStripTypename(value, typenameConfig) + keyVal[1] = + typenameConfig ? + maybeStripTypename(value, typenameConfig) : stripTypename(value); return keyVal; @@ -81,8 +102,9 @@ function maybeStripTypename( const fieldConfig = config[key]; - modified[key] = fieldConfig - ? maybeStripTypename(child, fieldConfig) + modified[key] = + fieldConfig ? + maybeStripTypename(child, fieldConfig) : stripTypename(child); }); @@ -92,17 +114,25 @@ function maybeStripTypename( return value; } -const getVariableDefinitions = wrap((document: DocumentNode) => { - const definitions: Record = {}; +const getVariableDefinitions = wrap( + (document: DocumentNode) => { + const definitions: Record = {}; - visit(document, { - VariableDefinition(node) { - definitions[node.variable.name.value] = unwrapType(node.type); - }, - }); + visit(document, { + VariableDefinition(node) { + definitions[node.variable.name.value] = unwrapType(node.type); + }, + }); - return definitions; -}); + return definitions; + }, + { + max: + cacheSizes["removeTypenameFromVariables.getVariableDefinitions"] || + defaultCacheSizes["removeTypenameFromVariables.getVariableDefinitions"], + cache: WeakCache, + } +); function unwrapType(node: TypeNode): string { switch (node.kind) { diff --git a/src/link/retry/__tests__/retryLink.ts b/src/link/retry/__tests__/retryLink.ts index 3f5413e5b39..b9f3e14440d 100644 --- a/src/link/retry/__tests__/retryLink.ts +++ b/src/link/retry/__tests__/retryLink.ts @@ -92,7 +92,12 @@ describe("RetryLink", () => { expect(unsubscribeStub).toHaveBeenCalledTimes(1); }); - it("supports multiple subscribers to the same request", async () => { + it("multiple subscribers will trigger multiple requests", async () => { + const subscriber = { + next: jest.fn(console.log), + error: jest.fn(console.error), + complete: jest.fn(console.info), + }; const retry = new RetryLink({ delay: { initial: 1 }, attempts: { max: 5 }, @@ -102,13 +107,19 @@ describe("RetryLink", () => { stub.mockReturnValueOnce(fromError(standardError)); stub.mockReturnValueOnce(fromError(standardError)); stub.mockReturnValueOnce(Observable.of(data)); + stub.mockReturnValueOnce(fromError(standardError)); + stub.mockReturnValueOnce(fromError(standardError)); + stub.mockReturnValueOnce(Observable.of(data)); const link = ApolloLink.from([retry, stub]); const observable = execute(link, { query }); - const [result1, result2] = (await waitFor(observable, observable)) as any; - expect(result1.values).toEqual([data]); - expect(result2.values).toEqual([data]); - expect(stub).toHaveBeenCalledTimes(3); + observable.subscribe(subscriber); + observable.subscribe(subscriber); + await new Promise((resolve) => setTimeout(resolve, 3500)); + expect(subscriber.next).toHaveBeenNthCalledWith(1, data); + expect(subscriber.next).toHaveBeenNthCalledWith(2, data); + expect(subscriber.complete).toHaveBeenCalledTimes(2); + expect(stub).toHaveBeenCalledTimes(6); }); it("retries independently for concurrent requests", async () => { diff --git a/src/link/retry/retryLink.ts b/src/link/retry/retryLink.ts index d44a382500c..cde2dd2ea9c 100644 --- a/src/link/retry/retryLink.ts +++ b/src/link/retry/retryLink.ts @@ -1,14 +1,12 @@ import type { Operation, FetchResult, NextLink } from "../core/index.js"; import { ApolloLink } from "../core/index.js"; -import type { - Observer, - ObservableSubscription, -} from "../../utilities/index.js"; +import type { ObservableSubscription } from "../../utilities/index.js"; import { Observable } from "../../utilities/index.js"; import type { DelayFunction, DelayFunctionOptions } from "./delayFunction.js"; import { buildDelayFunction } from "./delayFunction.js"; import type { RetryFunction, RetryFunctionOptions } from "./retryFunction.js"; import { buildRetryFunction } from "./retryFunction.js"; +import type { SubscriptionObserver } from "zen-observable-ts"; export namespace RetryLink { export interface Options { @@ -27,78 +25,18 @@ export namespace RetryLink { /** * Tracking and management of operations that may be (or currently are) retried. */ -class RetryableOperation { +class RetryableOperation { private retryCount: number = 0; - private values: any[] = []; - private error: any; - private complete = false; - private canceled = false; - private observers: (Observer | null)[] = []; private currentSubscription: ObservableSubscription | null = null; private timerId: number | undefined; constructor( + private observer: SubscriptionObserver, private operation: Operation, - private nextLink: NextLink, + private forward: NextLink, private delayFor: DelayFunction, private retryIf: RetryFunction - ) {} - - /** - * Register a new observer for this operation. - * - * If the operation has previously emitted other events, they will be - * immediately triggered for the observer. - */ - public subscribe(observer: Observer) { - if (this.canceled) { - throw new Error( - `Subscribing to a retryable link that was canceled is not supported` - ); - } - this.observers.push(observer); - - // If we've already begun, catch this observer up. - for (const value of this.values) { - observer.next!(value); - } - - if (this.complete) { - observer.complete!(); - } else if (this.error) { - observer.error!(this.error); - } - } - - /** - * Remove a previously registered observer from this operation. - * - * If no observers remain, the operation will stop retrying, and unsubscribe - * from its downstream link. - */ - public unsubscribe(observer: Observer) { - const index = this.observers.indexOf(observer); - if (index < 0) { - throw new Error( - `RetryLink BUG! Attempting to unsubscribe unknown observer!` - ); - } - // Note that we are careful not to change the order of length of the array, - // as we are often mid-iteration when calling this method. - this.observers[index] = null; - - // If this is the last observer, we're done. - if (this.observers.every((o) => o === null)) { - this.cancel(); - } - } - - /** - * Start the initial request. - */ - public start() { - if (this.currentSubscription) return; // Already started. - + ) { this.try(); } @@ -112,33 +50,16 @@ class RetryableOperation { clearTimeout(this.timerId); this.timerId = undefined; this.currentSubscription = null; - this.canceled = true; } private try() { - this.currentSubscription = this.nextLink(this.operation).subscribe({ - next: this.onNext, + this.currentSubscription = this.forward(this.operation).subscribe({ + next: this.observer.next.bind(this.observer), error: this.onError, - complete: this.onComplete, + complete: this.observer.complete.bind(this.observer), }); } - private onNext = (value: any) => { - this.values.push(value); - for (const observer of this.observers) { - if (!observer) continue; - observer.next!(value); - } - }; - - private onComplete = () => { - this.complete = true; - for (const observer of this.observers) { - if (!observer) continue; - observer.complete!(); - } - }; - private onError = async (error: any) => { this.retryCount += 1; @@ -153,11 +74,7 @@ class RetryableOperation { return; } - this.error = error; - for (const observer of this.observers) { - if (!observer) continue; - observer.error!(error); - } + this.observer.error(error); }; private scheduleRetry(delay: number) { @@ -189,18 +106,16 @@ export class RetryLink extends ApolloLink { operation: Operation, nextLink: NextLink ): Observable { - const retryable = new RetryableOperation( - operation, - nextLink, - this.delayFor, - this.retryIf - ); - retryable.start(); - return new Observable((observer) => { - retryable.subscribe(observer); + const retryable = new RetryableOperation( + observer, + operation, + nextLink, + this.delayFor, + this.retryIf + ); return () => { - retryable.unsubscribe(observer); + retryable.cancel(); }; }); } diff --git a/src/link/schema/index.ts b/src/link/schema/index.ts index 7809ec8f8a2..336fd9c9870 100644 --- a/src/link/schema/index.ts +++ b/src/link/schema/index.ts @@ -53,9 +53,9 @@ export class SchemaLink extends ApolloLink { return new Observable((observer) => { new Promise((resolve) => resolve( - typeof this.context === "function" - ? this.context(operation) - : this.context + typeof this.context === "function" ? + this.context(operation) + : this.context ) ) .then((context) => { diff --git a/src/link/utils/createOperation.ts b/src/link/utils/createOperation.ts index db9ad4b48a8..5093711a54f 100644 --- a/src/link/utils/createOperation.ts +++ b/src/link/utils/createOperation.ts @@ -5,14 +5,14 @@ export function createOperation( operation: GraphQLRequest ): Operation { let context = { ...starting }; - const setContext = (next: any) => { + const setContext: Operation["setContext"] = (next) => { if (typeof next === "function") { context = { ...context, ...next(context) }; } else { context = { ...context, ...next }; } }; - const getContext = () => ({ ...context }); + const getContext: Operation["getContext"] = () => ({ ...context }); Object.defineProperty(operation, "setContext", { enumerable: false, diff --git a/src/link/utils/transformOperation.ts b/src/link/utils/transformOperation.ts index 9e640bb97c4..7509662d5c7 100644 --- a/src/link/utils/transformOperation.ts +++ b/src/link/utils/transformOperation.ts @@ -12,9 +12,9 @@ export function transformOperation(operation: GraphQLRequest): GraphQLRequest { // Best guess at an operation name if (!transformedOperation.operationName) { transformedOperation.operationName = - typeof transformedOperation.query !== "string" - ? getOperationName(transformedOperation.query) || undefined - : ""; + typeof transformedOperation.query !== "string" ? + getOperationName(transformedOperation.query) || undefined + : ""; } return transformedOperation as Operation; diff --git a/src/react/cache/QueryReference.ts b/src/react/cache/QueryReference.ts deleted file mode 100644 index f1ad5732bac..00000000000 --- a/src/react/cache/QueryReference.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { equal } from "@wry/equality"; -import type { - ApolloError, - ApolloQueryResult, - ObservableQuery, - OperationVariables, - WatchQueryOptions, -} from "../../core/index.js"; -import { isNetworkRequestSettled } from "../../core/index.js"; -import type { ObservableSubscription } from "../../utilities/index.js"; -import { - createFulfilledPromise, - createRejectedPromise, -} from "../../utilities/index.js"; -import type { CacheKey } from "./types.js"; -import type { useBackgroundQuery, useReadQuery } from "../hooks/index.js"; - -type Listener = (promise: Promise>) => void; - -type FetchMoreOptions = Parameters< - ObservableQuery["fetchMore"] ->[0]; - -const QUERY_REFERENCE_SYMBOL: unique symbol = Symbol(); -/** - * A `QueryReference` is an opaque object returned by {@link useBackgroundQuery}. - * A child component reading the `QueryReference` via {@link useReadQuery} will - * suspend until the promise resolves. - */ -export interface QueryReference { - [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; -} - -interface InternalQueryReferenceOptions { - key: CacheKey; - onDispose?: () => void; - autoDisposeTimeoutMs?: number; -} - -export function wrapQueryRef( - internalQueryRef: InternalQueryReference -): QueryReference { - return { [QUERY_REFERENCE_SYMBOL]: internalQueryRef }; -} - -export function unwrapQueryRef( - queryRef: QueryReference -): InternalQueryReference { - return queryRef[QUERY_REFERENCE_SYMBOL]; -} - -const OBSERVED_CHANGED_OPTIONS = [ - "canonizeResults", - "context", - "errorPolicy", - "fetchPolicy", - "refetchWritePolicy", - "returnPartialData", -] as const; - -type ObservedOptions = Pick< - WatchQueryOptions, - (typeof OBSERVED_CHANGED_OPTIONS)[number] ->; - -export class InternalQueryReference { - public result: ApolloQueryResult; - public readonly key: CacheKey; - public readonly observable: ObservableQuery; - - public promiseCache?: Map>>; - public promise: Promise>; - - private subscription: ObservableSubscription; - private listeners = new Set>(); - private autoDisposeTimeoutId: NodeJS.Timeout; - private status: "idle" | "loading" = "loading"; - - private resolve: ((result: ApolloQueryResult) => void) | undefined; - private reject: ((error: unknown) => void) | undefined; - - private references = 0; - - constructor( - observable: ObservableQuery, - options: InternalQueryReferenceOptions - ) { - this.handleNext = this.handleNext.bind(this); - this.handleError = this.handleError.bind(this); - this.dispose = this.dispose.bind(this); - this.observable = observable; - // Don't save this result as last result to prevent delivery of last result - // when first subscribing - this.result = observable.getCurrentResult(false); - this.key = options.key; - - if (options.onDispose) { - this.onDispose = options.onDispose; - } - - if ( - isNetworkRequestSettled(this.result.networkStatus) || - (this.result.data && - (!this.result.partial || this.watchQueryOptions.returnPartialData)) - ) { - this.promise = createFulfilledPromise(this.result); - this.status = "idle"; - } else { - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - } - - this.subscription = observable - .filter(({ data }) => !equal(data, {})) - .subscribe({ - next: this.handleNext, - error: this.handleError, - }); - - // Start a timer that will automatically dispose of the query if the - // suspended resource does not use this queryRef in the given time. This - // helps prevent memory leaks when a component has unmounted before the - // query has finished loading. - const startDisposeTimer = () => { - if (!this.references) { - this.autoDisposeTimeoutId = setTimeout( - this.dispose, - options.autoDisposeTimeoutMs ?? 30_000 - ); - } - }; - - // We wait until the request has settled to ensure we don't dispose of the - // query ref before the request finishes, otherwise we would leave the - // promise in a pending state rendering the suspense boundary indefinitely. - this.promise.then(startDisposeTimer, startDisposeTimer); - } - - get watchQueryOptions() { - return this.observable.options; - } - - retain() { - this.references++; - clearTimeout(this.autoDisposeTimeoutId); - let disposed = false; - - return () => { - if (disposed) { - return; - } - - disposed = true; - this.references--; - - // Wait before fully disposing in case the app is running in strict mode. - setTimeout(() => { - if (!this.references) { - this.dispose(); - } - }); - }; - } - - didChangeOptions(watchQueryOptions: ObservedOptions) { - return OBSERVED_CHANGED_OPTIONS.some( - (option) => - !equal(this.watchQueryOptions[option], watchQueryOptions[option]) - ); - } - - applyOptions(watchQueryOptions: ObservedOptions) { - const { - fetchPolicy: currentFetchPolicy, - canonizeResults: currentCanonizeResults, - } = this.watchQueryOptions; - - // "standby" is used when `skip` is set to `true`. Detect when we've - // enabled the query (i.e. `skip` is `false`) to execute a network request. - if ( - currentFetchPolicy === "standby" && - currentFetchPolicy !== watchQueryOptions.fetchPolicy - ) { - this.initiateFetch(this.observable.reobserve(watchQueryOptions)); - } else { - this.observable.silentSetOptions(watchQueryOptions); - - if (currentCanonizeResults !== watchQueryOptions.canonizeResults) { - this.result = { ...this.result, ...this.observable.getCurrentResult() }; - this.promise = createFulfilledPromise(this.result); - } - } - - return this.promise; - } - - listen(listener: Listener) { - this.listeners.add(listener); - - return () => { - this.listeners.delete(listener); - }; - } - - refetch(variables: OperationVariables | undefined) { - return this.initiateFetch(this.observable.refetch(variables)); - } - - fetchMore(options: FetchMoreOptions) { - return this.initiateFetch(this.observable.fetchMore(options)); - } - - private dispose() { - this.subscription.unsubscribe(); - this.onDispose(); - } - - private onDispose() { - // noop. overridable by options - } - - private handleNext(result: ApolloQueryResult) { - switch (this.status) { - case "loading": { - // Maintain the last successful `data` value if the next result does not - // have one. - if (result.data === void 0) { - result.data = this.result.data; - } - this.status = "idle"; - this.result = result; - this.resolve?.(result); - break; - } - case "idle": { - // This occurs when switching to a result that is fully cached when this - // class is instantiated. ObservableQuery will run reobserve when - // subscribing, which delivers a result from the cache. - if (result.data === this.result.data) { - return; - } - - // Maintain the last successful `data` value if the next result does not - // have one. - if (result.data === void 0) { - result.data = this.result.data; - } - - this.result = result; - this.promise = createFulfilledPromise(result); - this.deliver(this.promise); - break; - } - } - } - - private handleError(error: ApolloError) { - this.subscription.unsubscribe(); - this.subscription = this.observable.resubscribeAfterError( - this.handleNext, - this.handleError - ); - - switch (this.status) { - case "loading": { - this.status = "idle"; - this.reject?.(error); - break; - } - case "idle": { - this.promise = createRejectedPromise(error); - this.deliver(this.promise); - } - } - } - - private deliver(promise: Promise>) { - this.listeners.forEach((listener) => listener(promise)); - } - - private initiateFetch(returnedPromise: Promise>) { - this.status = "loading"; - - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - - this.promise.catch(() => {}); - - // If the data returned from the fetch is deeply equal to the data already - // in the cache, `handleNext` will not be triggered leaving the promise we - // created in a pending state forever. To avoid this situtation, we attempt - // to resolve the promise if `handleNext` hasn't been run to ensure the - // promise is resolved correctly. - returnedPromise - .then((result) => { - if (this.status === "loading") { - this.status = "idle"; - this.result = result; - this.resolve?.(result); - } - }) - .catch(() => {}); - - return returnedPromise; - } -} diff --git a/src/react/cache/index.ts b/src/react/cache/index.ts deleted file mode 100644 index 25a6d03bee5..00000000000 --- a/src/react/cache/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { SuspenseCacheOptions } from "./SuspenseCache.js"; -export { getSuspenseCache } from "./getSuspenseCache.js"; diff --git a/src/react/components/Mutation.tsx b/src/react/components/Mutation.tsx index 492e0089312..ff122a7e648 100644 --- a/src/react/components/Mutation.tsx +++ b/src/react/components/Mutation.tsx @@ -1,12 +1,19 @@ import * as PropTypes from "prop-types"; +import type * as ReactTypes from "react"; import type { OperationVariables } from "../../core/index.js"; import type { MutationComponentOptions } from "./types.js"; import { useMutation } from "../hooks/index.js"; +/** + * @deprecated + * Official support for React Apollo render prop components ended in March 2020. + * This library is still included in the `@apollo/client` package, + * but it no longer receives feature updates or bug fixes. + */ export function Mutation( props: MutationComponentOptions -) { +): ReactTypes.JSX.Element | null { const [runMutation, result] = useMutation(props.mutation, props); return props.children ? props.children(runMutation, result) : null; } diff --git a/src/react/components/Query.tsx b/src/react/components/Query.tsx index 1e5611f646a..428207784c7 100644 --- a/src/react/components/Query.tsx +++ b/src/react/components/Query.tsx @@ -1,13 +1,22 @@ import * as PropTypes from "prop-types"; +import type * as ReactTypes from "react"; import type { OperationVariables } from "../../core/index.js"; import type { QueryComponentOptions } from "./types.js"; import { useQuery } from "../hooks/index.js"; +/** + * @deprecated + * Official support for React Apollo render prop components ended in March 2020. + * This library is still included in the `@apollo/client` package, + * but it no longer receives feature updates or bug fixes. + */ export function Query< TData = any, TVariables extends OperationVariables = OperationVariables, ->(props: QueryComponentOptions) { +>( + props: QueryComponentOptions +): ReactTypes.JSX.Element | null { const { children, query, ...options } = props; const result = useQuery(query, options); return result ? children(result as any) : null; diff --git a/src/react/components/Subscription.tsx b/src/react/components/Subscription.tsx index 5701cdcb01d..59d694156a5 100644 --- a/src/react/components/Subscription.tsx +++ b/src/react/components/Subscription.tsx @@ -1,13 +1,22 @@ import * as PropTypes from "prop-types"; +import type * as ReactTypes from "react"; import type { OperationVariables } from "../../core/index.js"; import type { SubscriptionComponentOptions } from "./types.js"; import { useSubscription } from "../hooks/index.js"; +/** + * @deprecated + * Official support for React Apollo render prop components ended in March 2020. + * This library is still included in the `@apollo/client` package, + * but it no longer receives feature updates or bug fixes. + */ export function Subscription< TData = any, TVariables extends OperationVariables = OperationVariables, ->(props: SubscriptionComponentOptions) { +>( + props: SubscriptionComponentOptions +): ReactTypes.JSX.Element | null { const result = useSubscription(props.subscription, props); return props.children && result ? props.children(result) : null; } diff --git a/src/react/components/__tests__/client/Mutation.test.tsx b/src/react/components/__tests__/client/Mutation.test.tsx index 80c75f99506..b71ba82f7cc 100644 --- a/src/react/components/__tests__/client/Mutation.test.tsx +++ b/src/react/components/__tests__/client/Mutation.test.tsx @@ -1348,7 +1348,7 @@ describe("General Mutation testing", () => { if (count === 0) { expect(result.called).toEqual(false); expect(result.loading).toEqual(false); - createTodo(); + setTimeout(createTodo, 10); } else if (count === 2 && result) { expect(result.data).toEqual(data); setTimeout(() => { @@ -1358,7 +1358,7 @@ describe("General Mutation testing", () => { }); } else if (count === 3) { expect(result.loading).toEqual(false); - createTodo(); + setTimeout(createTodo, 10); } else if (count === 5) { expect(result.data).toEqual(data3); } diff --git a/src/react/components/__tests__/client/Query.test.tsx b/src/react/components/__tests__/client/Query.test.tsx index 3aee25d2685..9182e61e19e 100644 --- a/src/react/components/__tests__/client/Query.test.tsx +++ b/src/react/components/__tests__/client/Query.test.tsx @@ -369,16 +369,16 @@ describe("Query component", () => { .fetchMore({ variables: { first: 1 }, updateQuery: (prev: any, { fetchMoreResult }: any) => - fetchMoreResult - ? { - allPeople: { - people: [ - ...prev.allPeople.people, - ...fetchMoreResult.allPeople.people, - ], - }, - } - : prev, + fetchMoreResult ? + { + allPeople: { + people: [ + ...prev.allPeople.people, + ...fetchMoreResult.allPeople.people, + ], + }, + } + : prev, }) .then((result2: any) => { expect(result2.data).toEqual(data2); @@ -1491,7 +1491,7 @@ describe("Query component", () => { return ( {(r: any) => { - ProfiledContainer.updateSnapshot(r); + ProfiledContainer.replaceSnapshot(r); return null; }} diff --git a/src/react/components/types.ts b/src/react/components/types.ts index 4e1abacb6a1..a742b905ac6 100644 --- a/src/react/components/types.ts +++ b/src/react/components/types.ts @@ -1,6 +1,8 @@ import type { DocumentNode } from "graphql"; import type { TypedDocumentNode } from "@graphql-typed-document-node/core"; +import type * as ReactTypes from "react"; + import type { OperationVariables, DefaultContext, @@ -20,7 +22,9 @@ export interface QueryComponentOptions< TData = any, TVariables extends OperationVariables = OperationVariables, > extends QueryFunctionOptions { - children: (result: QueryResult) => JSX.Element | null; + children: ( + result: QueryResult + ) => ReactTypes.JSX.Element | null; query: DocumentNode | TypedDocumentNode; } @@ -34,13 +38,16 @@ export interface MutationComponentOptions< children: ( mutateFunction: MutationFunction, result: MutationResult - ) => JSX.Element | null; + ) => ReactTypes.JSX.Element | null; } export interface SubscriptionComponentOptions< TData = any, TVariables extends OperationVariables = OperationVariables, > extends BaseSubscriptionOptions { + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#query:member} */ subscription: DocumentNode | TypedDocumentNode; - children?: null | ((result: SubscriptionResult) => JSX.Element | null); + children?: + | null + | ((result: SubscriptionResult) => ReactTypes.JSX.Element | null); } diff --git a/src/react/context/ApolloConsumer.tsx b/src/react/context/ApolloConsumer.tsx index ac26e734da9..b50cbd2893d 100644 --- a/src/react/context/ApolloConsumer.tsx +++ b/src/react/context/ApolloConsumer.tsx @@ -1,15 +1,16 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { ApolloClient } from "../../core/index.js"; import { getApolloContext } from "./ApolloContext.js"; export interface ApolloConsumerProps { - children: (client: ApolloClient) => React.ReactChild | null; + children: (client: ApolloClient) => ReactTypes.ReactNode; } -export const ApolloConsumer: React.FC = (props) => { +export const ApolloConsumer: ReactTypes.FC = (props) => { const ApolloContext = getApolloContext(); return ( diff --git a/src/react/context/ApolloContext.ts b/src/react/context/ApolloContext.ts index e942e8e9dad..e0646a43431 100644 --- a/src/react/context/ApolloContext.ts +++ b/src/react/context/ApolloContext.ts @@ -1,4 +1,5 @@ -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { ApolloClient } from "../../core/index.js"; import { canUseSymbol } from "../../utilities/index.js"; import type { RenderPromises } from "../ssr/index.js"; @@ -13,11 +14,10 @@ export interface ApolloContextValue { // (which can lead to problems like having an Apollo Client instance added // in one context, then attempting to retrieve it from another different // context), a single Apollo context is created and tracked in global state. -const contextKey = canUseSymbol - ? Symbol.for("__APOLLO_CONTEXT__") - : "__APOLLO_CONTEXT__"; +const contextKey = + canUseSymbol ? Symbol.for("__APOLLO_CONTEXT__") : "__APOLLO_CONTEXT__"; -export function getApolloContext(): React.Context { +export function getApolloContext(): ReactTypes.Context { invariant( "createContext" in React, "Invoking `getApolloContext` in an environment where `React.createContext` is not available.\n" + diff --git a/src/react/context/ApolloProvider.tsx b/src/react/context/ApolloProvider.tsx index 930e32dab0a..aa91b2cfc01 100644 --- a/src/react/context/ApolloProvider.tsx +++ b/src/react/context/ApolloProvider.tsx @@ -1,16 +1,17 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { ApolloClient } from "../../core/index.js"; import { getApolloContext } from "./ApolloContext.js"; export interface ApolloProviderProps { client: ApolloClient; - children: React.ReactNode | React.ReactNode[] | null; + children: ReactTypes.ReactNode | ReactTypes.ReactNode[] | null; } -export const ApolloProvider: React.FC> = ({ +export const ApolloProvider: ReactTypes.FC> = ({ client, children, }) => { diff --git a/src/react/hoc/__tests__/fragments.test.tsx b/src/react/hoc/__tests__/fragments.test.tsx index 1597a4b7092..741e5e2f8f7 100644 --- a/src/react/hoc/__tests__/fragments.test.tsx +++ b/src/react/hoc/__tests__/fragments.test.tsx @@ -55,7 +55,7 @@ describe("fragments", () => { ); throw new Error(); } catch (e) { - expect(e.name).toMatch(/Invariant Violation/); + expect((e as Error).name).toMatch(/Invariant Violation/); } }); diff --git a/src/react/hoc/__tests__/mutations/lifecycle.test.tsx b/src/react/hoc/__tests__/mutations/lifecycle.test.tsx index 0c0b88efd8e..f6ff711e48d 100644 --- a/src/react/hoc/__tests__/mutations/lifecycle.test.tsx +++ b/src/react/hoc/__tests__/mutations/lifecycle.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, cleanup } from "@testing-library/react"; +import { render } from "@testing-library/react"; import gql from "graphql-tag"; import { ApolloProvider } from "../../../context/ApolloProvider"; @@ -21,8 +21,6 @@ const expectedData = { }; describe("graphql(mutation) lifecycle", () => { - afterEach(cleanup); - itAsync( "allows falsy values in the mapped variables from props", (resolve, reject) => { @@ -94,7 +92,9 @@ describe("graphql(mutation) lifecycle", () => { class Container extends React.Component> { render() { if (this.props.listId !== 2) return null; - this.props.mutate!().then(() => resolve()); + setTimeout(() => { + this.props.mutate!().then(() => resolve()); + }); return null; } } diff --git a/src/react/hoc/__tests__/queries/errors.test.tsx b/src/react/hoc/__tests__/queries/errors.test.tsx index 449e3c46c67..dfe92de05ce 100644 --- a/src/react/hoc/__tests__/queries/errors.test.tsx +++ b/src/react/hoc/__tests__/queries/errors.test.tsx @@ -105,7 +105,7 @@ describe("[queries] errors", () => { try { unmount(); - } catch (e) { + } catch (e: any) { throw new Error(e); } }); @@ -220,6 +220,7 @@ describe("[queries] errors", () => { "setVar", 1 )( + // @ts-expect-error graphql(query)( class extends React.Component> { componentDidUpdate() { diff --git a/src/react/hoc/__tests__/queries/lifecycle.test.tsx b/src/react/hoc/__tests__/queries/lifecycle.test.tsx index 99e7fbc6c5e..cf460af964a 100644 --- a/src/react/hoc/__tests__/queries/lifecycle.test.tsx +++ b/src/react/hoc/__tests__/queries/lifecycle.test.tsx @@ -52,7 +52,7 @@ describe("[queries] lifecycle", () => { })( class extends React.Component> { render() { - ProfiledApp.updateSnapshot(this.props.data!); + ProfiledApp.replaceSnapshot(this.props.data!); return null; } } diff --git a/src/react/hoc/__tests__/queries/loading.test.tsx b/src/react/hoc/__tests__/queries/loading.test.tsx index c2a40c0b9ec..387a6803fb5 100644 --- a/src/react/hoc/__tests__/queries/loading.test.tsx +++ b/src/react/hoc/__tests__/queries/loading.test.tsx @@ -407,7 +407,7 @@ describe("[queries] loading", () => { })( class extends React.Component> { render() { - ProfiledContainer.updateSnapshot(this.props.data!); + ProfiledContainer.replaceSnapshot(this.props.data!); return null; } } diff --git a/src/react/hoc/__tests__/queries/observableQuery.test.tsx b/src/react/hoc/__tests__/queries/observableQuery.test.tsx index dd05663aac2..c05755b115b 100644 --- a/src/react/hoc/__tests__/queries/observableQuery.test.tsx +++ b/src/react/hoc/__tests__/queries/observableQuery.test.tsx @@ -64,7 +64,7 @@ describe("[queries] observableQuery", () => { // ensure first assertion and umount tree await assert1(); - userEvent.click(screen.getByText("Break things")); + await userEvent.click(screen.getByText("Break things")); // ensure cleanup await assert2(); diff --git a/src/react/hoc/__tests__/queries/recomposeWithState.ts b/src/react/hoc/__tests__/queries/recomposeWithState.ts index a1bde0a6c18..2ec2ad79756 100644 --- a/src/react/hoc/__tests__/queries/recomposeWithState.ts +++ b/src/react/hoc/__tests__/queries/recomposeWithState.ts @@ -40,9 +40,9 @@ export const withState = > { state = { stateValue: - typeof initialState === "function" - ? initialState(this.props) - : initialState, + typeof initialState === "function" ? + initialState(this.props) + : initialState, }; updateStateValue = ( diff --git a/src/react/hoc/__tests__/queries/updateQuery.test.tsx b/src/react/hoc/__tests__/queries/updateQuery.test.tsx index 0d3056b862e..b11de81c326 100644 --- a/src/react/hoc/__tests__/queries/updateQuery.test.tsx +++ b/src/react/hoc/__tests__/queries/updateQuery.test.tsx @@ -143,7 +143,7 @@ describe("[queries] updateQuery", () => { ).toBeTruthy(); try { this.props.data!.updateQuery((p) => p); - } catch (e) { + } catch (e: any) { // TODO: branch never hit in test expect(e.toString()).toMatch( /ObservableQuery with this id doesn't exist:/ diff --git a/src/react/hoc/__tests__/shared-operations.test.tsx b/src/react/hoc/__tests__/shared-operations.test.tsx index efd0b12924c..39c3561998a 100644 --- a/src/react/hoc/__tests__/shared-operations.test.tsx +++ b/src/react/hoc/__tests__/shared-operations.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, cleanup } from "@testing-library/react"; +import { render } from "@testing-library/react"; import gql from "graphql-tag"; import { DocumentNode } from "graphql"; @@ -25,8 +25,6 @@ function compose(...funcs: Function[]) { } describe("shared operations", () => { - afterEach(cleanup); - describe("withApollo", () => { it("passes apollo-client to props", () => { const client = new ApolloClient({ @@ -92,7 +90,7 @@ describe("shared operations", () => { people: DataValue; } - // Since we want to test decorators usage, and this does not play well with Typescript, + // Since we want to test decorators usage, and this does not play well with TypeScript, // we resort to setting everything as any to avoid type checking. const withPeople: any = graphql<{}, PeopleData, {}, PeopleChildProps>( peopleQuery, diff --git a/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx b/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx index b4a0ab6fc6c..0a687741f64 100644 --- a/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx +++ b/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx @@ -52,9 +52,9 @@ describe("SSR", () => { const WrappedElement = graphql(query)( ({ data }: ChildProps) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
) ); @@ -105,9 +105,9 @@ describe("SSR", () => { options: { fetchPolicy: "network-only" }, })(({ data }: ChildProps) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
)); @@ -191,9 +191,9 @@ describe("SSR", () => { const WrappedElement = graphql(query)( ({ data }: ChildProps) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
) ); @@ -282,9 +282,9 @@ describe("SSR", () => { React.PropsWithChildren> > = ({ data }) => (
- {!data || data.loading || !data.user - ? "loading" - : data.user.firstName} + {!data || data.loading || !data.user ? + "loading" + : data.user.firstName}
); @@ -365,9 +365,9 @@ describe("SSR", () => { React.PropsWithChildren> > = ({ data }) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.lastName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.lastName}
@@ -521,9 +521,9 @@ describe("SSR", () => { const Element = graphql(query)( ({ data }: ChildProps) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
) ); @@ -595,9 +595,9 @@ describe("SSR", () => { expect(this.state.thing).toBe(2); return (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
); } @@ -714,9 +714,9 @@ describe("SSR", () => { expect(this.state.client).toBe(apolloClient); return (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
); } @@ -785,9 +785,9 @@ describe("SSR", () => { options: (props) => ({ variables: props, ssr: false }), })(({ data }) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
)); @@ -939,12 +939,13 @@ describe("SSR", () => { > > = ({ data }) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
); + // @ts-expect-error const WrappedElement = withQuery(withMutation(Element)); const app = ( @@ -1023,9 +1024,9 @@ describe("SSR", () => { > > = ({ data }) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
); @@ -1068,9 +1069,9 @@ describe("SSR", () => { const WrappedElement = graphql<{}, Data>(query)( ({ data }: ChildProps<{}, Data>) => (
- {!data || data.loading || !data.currentUser - ? "loading" - : data.currentUser.firstName} + {!data || data.loading || !data.currentUser ? + "loading" + : data.currentUser.firstName}
) ); diff --git a/src/react/hoc/graphql.tsx b/src/react/hoc/graphql.tsx index 88bf39e9961..181bb5a951e 100644 --- a/src/react/hoc/graphql.tsx +++ b/src/react/hoc/graphql.tsx @@ -1,4 +1,5 @@ import type { DocumentNode } from "graphql"; +import type * as ReactTypes from "react"; import { parser, DocumentType } from "../parser/index.js"; import { withQuery } from "./query-hoc.js"; @@ -7,6 +8,11 @@ import { withSubscription } from "./subscription-hoc.js"; import type { OperationOption, DataProps, MutateProps } from "./types.js"; import type { OperationVariables } from "../../core/index.js"; +/** + * @deprecated + * Official support for React Apollo higher order components ended in March 2020. + * This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. + */ export function graphql< TProps extends TGraphQLVariables | {} = {}, TData extends object = {}, @@ -22,8 +28,8 @@ export function graphql< TChildProps > = {} ): ( - WrappedComponent: React.ComponentType -) => React.ComponentClass { + WrappedComponent: ReactTypes.ComponentType +) => ReactTypes.ComponentClass { switch (parser(document).type) { case DocumentType.Mutation: return withMutation(document, operationOptions); diff --git a/src/react/hoc/hoc-utils.tsx b/src/react/hoc/hoc-utils.tsx index 2e59f74e944..7c7d0598e08 100644 --- a/src/react/hoc/hoc-utils.tsx +++ b/src/react/hoc/hoc-utils.tsx @@ -1,5 +1,5 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; import type { OperationVariables } from "../../core/index.js"; import type { IDocumentDefinition } from "../parser/index.js"; diff --git a/src/react/hoc/mutation-hoc.tsx b/src/react/hoc/mutation-hoc.tsx index a0098a0f290..2620f46ff81 100644 --- a/src/react/hoc/mutation-hoc.tsx +++ b/src/react/hoc/mutation-hoc.tsx @@ -1,4 +1,5 @@ -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { DocumentNode } from "graphql"; import hoistNonReactStatics from "hoist-non-react-statics"; @@ -20,6 +21,11 @@ import { import type { OperationOption, OptionProps, MutateProps } from "./types.js"; import type { ApolloCache } from "../../core/index.js"; +/** + * @deprecated + * Official support for React Apollo higher order components ended in March 2020. + * This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. + */ export function withMutation< TProps extends TGraphQLVariables | {} = {}, TData extends Record = {}, @@ -56,8 +62,8 @@ export function withMutation< >; return ( - WrappedComponent: React.ComponentType - ): React.ComponentClass => { + WrappedComponent: ReactTypes.ComponentType + ): ReactTypes.ComponentClass => { const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`; class GraphQL extends GraphQLBase { static displayName = graphQLDisplayName; @@ -86,6 +92,7 @@ export function withMutation< return ( + {/* @ts-expect-error */} {( mutate: MutationFunction, { data, ...r }: MutationResult @@ -96,9 +103,8 @@ export function withMutation< // this matches the query HoC const result = Object.assign(r, data || {}); const name = operationOptions.name || "mutate"; - const resultName = operationOptions.name - ? `${name}Result` - : "result"; + const resultName = + operationOptions.name ? `${name}Result` : "result"; let childProps = { [name]: mutate, [resultName]: result, diff --git a/src/react/hoc/query-hoc.tsx b/src/react/hoc/query-hoc.tsx index 144133494c6..cdefd397916 100644 --- a/src/react/hoc/query-hoc.tsx +++ b/src/react/hoc/query-hoc.tsx @@ -1,4 +1,5 @@ -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { DocumentNode } from "graphql"; import hoistNonReactStatics from "hoist-non-react-statics"; @@ -14,6 +15,11 @@ import { } from "./hoc-utils.js"; import type { OperationOption, OptionProps, DataProps } from "./types.js"; +/** + * @deprecated + * Official support for React Apollo higher order components ended in March 2020. + * This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. + */ export function withQuery< TProps extends TGraphQLVariables | Record = Record, TData extends object = {}, @@ -50,8 +56,8 @@ export function withQuery< // allow for advanced referential equality checks let lastResultProps: TChildProps | void; return ( - WrappedComponent: React.ComponentType - ): React.ComponentClass => { + WrappedComponent: ReactTypes.ComponentType + ): ReactTypes.ComponentClass => { const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`; class GraphQL extends GraphQLBase { static displayName = graphQLDisplayName; @@ -60,9 +66,8 @@ export function withQuery< render() { let props = this.props; const shouldSkip = mapPropsToSkip(props); - const opts = shouldSkip - ? Object.create(null) - : { ...mapPropsToOptions(props) }; + const opts = + shouldSkip ? Object.create(null) : { ...mapPropsToOptions(props) }; if (!shouldSkip && !opts.variables && operation.variables.length > 0) { opts.variables = calculateVariablesFromProps(operation, props); diff --git a/src/react/hoc/subscription-hoc.tsx b/src/react/hoc/subscription-hoc.tsx index b044c1c2658..bb11060aaf7 100644 --- a/src/react/hoc/subscription-hoc.tsx +++ b/src/react/hoc/subscription-hoc.tsx @@ -1,4 +1,5 @@ -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import type { DocumentNode } from "graphql"; import hoistNonReactStatics from "hoist-non-react-statics"; @@ -14,6 +15,11 @@ import { } from "./hoc-utils.js"; import type { OperationOption, OptionProps, DataProps } from "./types.js"; +/** + * @deprecated + * Official support for React Apollo higher order components ended in March 2020. + * This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. + */ export function withSubscription< TProps extends TGraphQLVariables | {} = {}, TData extends object = {}, @@ -48,8 +54,8 @@ export function withSubscription< // allow for advanced referential equality checks let lastResultProps: TChildProps | void; return ( - WrappedComponent: React.ComponentType - ): React.ComponentClass => { + WrappedComponent: ReactTypes.ComponentType + ): ReactTypes.ComponentClass => { const graphQLDisplayName = `${alias}(${getDisplayName(WrappedComponent)})`; class GraphQL extends GraphQLBase< TProps, @@ -79,9 +85,8 @@ export function withSubscription< render() { let props = this.props; const shouldSkip = mapPropsToSkip(props); - const opts = shouldSkip - ? Object.create(null) - : mapPropsToOptions(props); + const opts = + shouldSkip ? Object.create(null) : mapPropsToOptions(props); if (!shouldSkip && !opts.variables && operation.variables.length > 0) { opts.variables = calculateVariablesFromProps(operation, props); diff --git a/src/react/hoc/withApollo.tsx b/src/react/hoc/withApollo.tsx index bfa21b454a4..54cb7c67be9 100644 --- a/src/react/hoc/withApollo.tsx +++ b/src/react/hoc/withApollo.tsx @@ -1,20 +1,26 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import hoistNonReactStatics from "hoist-non-react-statics"; import { ApolloConsumer } from "../context/index.js"; import type { OperationOption, WithApolloClient } from "./types.js"; -function getDisplayName

(WrappedComponent: React.ComponentType

) { +function getDisplayName

(WrappedComponent: ReactTypes.ComponentType

) { return WrappedComponent.displayName || WrappedComponent.name || "Component"; } +/** + * @deprecated + * Official support for React Apollo higher order components ended in March 2020. + * This library is still included in the `@apollo/client` package, but it no longer receives feature updates or bug fixes. + */ export function withApollo( - WrappedComponent: React.ComponentType< + WrappedComponent: ReactTypes.ComponentType< WithApolloClient> >, operationOptions: OperationOption = {} -): React.ComponentClass> { +): ReactTypes.ComponentClass> { const withDisplayName = `withApollo(${getDisplayName(WrappedComponent)})`; class WithApollo extends React.Component> { @@ -39,7 +45,9 @@ export function withApollo( return this.wrappedInstance; } - setWrappedInstance(ref: React.ComponentType>) { + setWrappedInstance( + ref: ReactTypes.ComponentType> + ) { this.wrappedInstance = ref; } @@ -49,9 +57,8 @@ export function withApollo( {(client) => { const props = Object.assign({}, this.props, { client, - ref: operationOptions.withRef - ? this.setWrappedInstance - : undefined, + ref: + operationOptions.withRef ? this.setWrappedInstance : undefined, }); return ; }} diff --git a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx index 08121af7f27..91d142a4df5 100644 --- a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx +++ b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx @@ -1,5782 +1,6492 @@ -import React, { ComponentProps, Fragment, StrictMode, Suspense } from "react"; -import { - act, - render, - screen, - screen as _screen, - renderHook, - RenderHookOptions, - waitFor, -} from "@testing-library/react"; +import React, { Suspense } from "react"; +import { act, screen, renderHook } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { ErrorBoundary, ErrorBoundaryProps } from "react-error-boundary"; +import { + ErrorBoundary as ReactErrorBoundary, + FallbackProps, +} from "react-error-boundary"; import { expectTypeOf } from "expect-type"; import { GraphQLError } from "graphql"; import { gql, ApolloError, - DocumentNode, ApolloClient, ErrorPolicy, - NormalizedCacheObject, NetworkStatus, - ApolloCache, TypedDocumentNode, ApolloLink, Observable, - FetchMoreQueryOptions, - OperationVariables, - ApolloQueryResult, } from "../../../core"; import { MockedResponse, - MockedProvider, MockLink, MockSubscriptionLink, mockSingleLink, + MockedProvider, + wait, } from "../../../testing"; import { concatPagination, offsetLimitPagination, DeepPartial, - cloneDeep, } from "../../../utilities"; import { useBackgroundQuery } from "../useBackgroundQuery"; -import { useReadQuery } from "../useReadQuery"; +import { UseReadQueryResult, useReadQuery } from "../useReadQuery"; import { ApolloProvider } from "../../context"; -import { unwrapQueryRef, QueryReference } from "../../cache/QueryReference"; +import { QueryRef, QueryReference } from "../../internal"; import { InMemoryCache } from "../../../cache"; -import { - SuspenseQueryHookFetchPolicy, - SuspenseQueryHookOptions, -} from "../../types/types"; +import { SuspenseQueryHookFetchPolicy } from "../../types/types"; import equal from "@wry/equality"; import { RefetchWritePolicy } from "../../../core/watchQueryOptions"; import { skipToken } from "../constants"; -import { profile, spyOnConsole } from "../../../testing/internal"; - -function renderIntegrationTest({ - client, -}: { - client?: ApolloClient; -} = {}) { - const query: TypedDocumentNode = gql` - query SimpleQuery { - foo { - bar - } - } - `; - - const mocks = [ - { - request: { query }, - result: { data: { foo: { bar: "hello" } } }, - }, - ]; - const _client = - client || - new ApolloClient({ - cache: new InMemoryCache(), - link: new MockLink(mocks), - }); - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - }; - const errorBoundaryProps: ErrorBoundaryProps = { - fallback:

Error
, - onError: (error) => { - renders.errorCount++; - renders.errors.push(error); - }, - }; - - interface QueryData { - foo: { bar: string }; - } +import { + PaginatedCaseData, + Profiler, + SimpleCaseData, + VariablesCaseData, + VariablesCaseVariables, + createProfiler, + renderWithClient, + renderWithMocks, + setupPaginatedCase, + setupSimpleCase, + setupVariablesCase, + spyOnConsole, + useTrackRenders, +} from "../../../testing/internal"; + +afterEach(() => { + jest.useRealTimers(); +}); +function createDefaultTrackedComponents< + Snapshot extends { result: UseReadQueryResult | null }, + TData = Snapshot["result"] extends UseReadQueryResult | null ? + TData + : unknown, +>(Profiler: Profiler) { function SuspenseFallback() { - renders.suspenseCount++; - return
loading
; + useTrackRenders(); + return
Loading
; } - function Child({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); - // count renders in the child component - renders.count++; - return
{data.foo.bar}
; - } + function ReadQueryHook({ queryRef }: { queryRef: QueryRef }) { + useTrackRenders(); + Profiler.mergeSnapshot({ + result: useReadQuery(queryRef), + } as Partial); - function Parent() { - const [queryRef] = useBackgroundQuery(query); - return ; + return null; } - function ParentWithVariables() { - const [queryRef] = useBackgroundQuery(query); - return ; + return { SuspenseFallback, ReadQueryHook }; +} + +function createTrackedErrorComponents( + Profiler: Profiler +) { + function ErrorFallback({ error }: FallbackProps) { + useTrackRenders({ name: "ErrorFallback" }); + Profiler.mergeSnapshot({ error } as Partial); + + return
Error
; } - function App({ variables }: { variables?: Record }) { + function ErrorBoundary({ children }: { children: React.ReactNode }) { return ( - - - }> - {variables ? : } - - - + + {children} + ); } - const { ...rest } = render(); - return { ...rest, query, client: _client, renders }; + return { ErrorBoundary }; } -interface VariablesCaseData { - character: { - id: string; - name: string; - }; +function createErrorProfiler() { + return createProfiler({ + initialSnapshot: { + error: null as Error | null, + result: null as UseReadQueryResult | null, + }, + }); } -interface VariablesCaseVariables { - id: string; +function createDefaultProfiler() { + return createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); } -function useVariablesIntegrationTestCase() { - const query: TypedDocumentNode< - VariablesCaseData, - VariablesCaseVariables - > = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - const CHARACTERS = ["Spider-Man", "Black Widow", "Iron Man", "Hulk"]; - let mocks = [...CHARACTERS].map((name, index) => ({ - request: { query, variables: { id: String(index + 1) } }, - result: { data: { character: { id: String(index + 1), name } } }, - })); - return { mocks, query }; -} +it("fetches a simple query with minimal config", async () => { + const { query, mocks } = setupSimpleCase(); -function renderVariablesIntegrationTest({ - variables, - mocks, - errorPolicy, - options, - cache, -}: { - mocks?: { - request: { query: DocumentNode; variables: { id: string } }; - result: { - data?: { - character: { - id: string; - name: string | null; - }; - }; - }; - }[]; - variables: { id: string }; - options?: SuspenseQueryHookOptions; - cache?: InMemoryCache; - errorPolicy?: ErrorPolicy; -}) { - let { mocks: _mocks, query } = useVariablesIntegrationTestCase(); - - // duplicate mocks with (updated) in the name for refetches - _mocks = [..._mocks, ..._mocks, ..._mocks].map( - ({ request, result }, index) => { - return { - request: request, - result: { - data: { - character: { - ...result.data.character, - name: - index > 3 - ? index > 7 - ? `${result.data.character.name} (updated again)` - : `${result.data.character.name} (updated)` - : result.data.character.name, - }, - }, - }, - }; - } - ); - const client = new ApolloClient({ - cache: cache || new InMemoryCache(), - link: new MockLink(mocks || _mocks), - }); - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: { - data: VariablesCaseData; - networkStatus: NetworkStatus; - error: ApolloError | undefined; - }[]; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; + const Profiler = createDefaultProfiler(); - const errorBoundaryProps: ErrorBoundaryProps = { - fallback:
Error
, - onError: (error) => { - renders.errorCount++; - renders.errors.push(error); - }, - }; + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function SuspenseFallback() { - renders.suspenseCount++; - return
loading
; - } - - function Child({ - refetch, - variables: _variables, - queryRef, - }: { - variables: VariablesCaseVariables; - refetch: ( - variables?: Partial | undefined - ) => Promise>; - queryRef: QueryReference; - }) { - const { data, error, networkStatus } = useReadQuery(queryRef); - const [variables, setVariables] = React.useState(_variables); - // count renders in the child component - renders.count++; - renders.frames.push({ data, networkStatus, error }); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); return ( -
- {error ?
{error.message}
: null} - - - {data?.character.id} - {data?.character.name} -
+ }> + + ); } - function ParentWithVariables({ - variables, - errorPolicy = "none", - }: { - variables: VariablesCaseVariables; - errorPolicy?: ErrorPolicy; - }) { - const [queryRef, { refetch }] = useBackgroundQuery(query, { - ...options, - variables, - errorPolicy, + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - return ( - - ); } - function App({ - variables, - errorPolicy, - }: { - variables: VariablesCaseVariables; - errorPolicy?: ErrorPolicy; - }) { + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it("tears down the query on unmount", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); + return ( - - - }> - - - - + }> + + ); } - const ProfiledApp = profile>({ - Component: App, - snapshotDOM: true, - onRender: ({ updateSnapshot }) => updateSnapshot(cloneDeep(renders)), - }); + const { unmount } = renderWithClient(, { client, wrapper: Profiler }); - const { ...rest } = render( - - ); - const rerender = ({ variables }: { variables: VariablesCaseVariables }) => { - return rest.rerender(); - }; - return { - ...rest, - ProfiledApp, - query, - rerender, - client, - renders, - mocks: mocks || _mocks, - }; -} + // initial suspended render + await Profiler.takeRender(); -function renderPaginatedIntegrationTest({ - updateQuery, - fieldPolicies, -}: { - fieldPolicies?: boolean; - updateQuery?: boolean; - mocks?: { - request: { - query: DocumentNode; - variables: { offset: number; limit: number }; - }; - result: { - data: { - letters: { - letter: string; - position: number; - }[]; - }; - }; - }[]; -} = {}) { - interface QueryData { - letters: { - letter: string; - position: number; - }[]; - } + { + const { snapshot } = await Profiler.takeRender(); - interface Variables { - limit?: number; - offset?: number; + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); } - const query: TypedDocumentNode = gql` - query letters($limit: Int, $offset: Int) { - letters(limit: $limit) { - letter - position - } - } - `; + unmount(); - const data = "ABCDEFG" - .split("") - .map((letter, index) => ({ letter, position: index + 1 })); + await wait(0); - const link = new ApolloLink((operation) => { - const { offset = 0, limit = 2 } = operation.variables; - const letters = data.slice(offset, offset + limit); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); - return new Observable((observer) => { - setTimeout(() => { - observer.next({ data: { letters } }); - observer.complete(); - }, 10); - }); +it("auto disposes of the queryRef if not used within timeout", async () => { + jest.useFakeTimers(); + const { query } = setupSimpleCase(); + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ link, cache: new InMemoryCache() }); + + const { result } = renderHook(() => useBackgroundQuery(query, { client })); + + const [queryRef] = result.current; + + expect(queryRef).not.toBeDisposed(); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + await act(() => { + link.simulateResult({ result: { data: { greeting: "Hello" } } }, true); + // Ensure simulateResult will deliver the result since its wrapped with + // setTimeout + jest.advanceTimersByTime(10); }); - const cacheWithTypePolicies = new InMemoryCache({ - typePolicies: { - Query: { - fields: { - letters: concatPagination(), + jest.advanceTimersByTime(30_000); + + expect(queryRef).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); + +it("auto disposes of the queryRef if not used within configured timeout", async () => { + jest.useFakeTimers(); + const { query } = setupSimpleCase(); + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + defaultOptions: { + react: { + suspense: { + autoDisposeTimeoutMs: 5000, }, }, }, }); - const client = new ApolloClient({ - cache: fieldPolicies ? cacheWithTypePolicies : new InMemoryCache(), - link, + + const { result } = renderHook(() => useBackgroundQuery(query, { client })); + + const [queryRef] = result.current; + + expect(queryRef).not.toBeDisposed(); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + await act(() => { + link.simulateResult({ result: { data: { greeting: "Hello" } } }, true); + // Ensure simulateResult will deliver the result since its wrapped with + // setTimeout + jest.advanceTimersByTime(10); }); - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - } - function SuspenseFallback() { - ProfiledApp.updateSnapshot((snapshot) => ({ - ...snapshot, - suspenseCount: snapshot.suspenseCount + 1, - })); - return
loading
; - } - - function Child({ - queryRef, - onFetchMore, - }: { - onFetchMore: (options: FetchMoreQueryOptions) => void; - queryRef: QueryReference; - }) { - const { data, error } = useReadQuery(queryRef); - // count renders in the child component - ProfiledApp.updateSnapshot((snapshot) => ({ - ...snapshot, - count: snapshot.count + 1, - })); - return ( -
- {error ?
{error.message}
: null} - -
    - {data.letters.map(({ letter, position }) => ( -
  • - {letter} -
  • - ))} -
-
- ); - } + expect(queryRef).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); - function ParentWithVariables() { - const [queryRef, { fetchMore }] = useBackgroundQuery(query, { - variables: { limit: 2, offset: 0 }, - }); - return ; - } +it("will resubscribe after disposed when mounting useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + defaultOptions: { + react: { + suspense: { + // Set this to something really low to avoid fake timers + autoDisposeTimeoutMs: 20, + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); function App() { + useTrackRenders(); + const [show, setShow] = React.useState(false); + const [queryRef] = useBackgroundQuery(query); + return ( - - Error} - onError={(error) => { - ProfiledApp.updateSnapshot((snapshot) => ({ - ...snapshot, - errorCount: snapshot.errorCount + 1, - errors: snapshot.errors.concat(error), - })); - }} - > - }> - - - - + <> + + }> + {show && } + + ); } - const ProfiledApp = profile({ - Component: App, - snapshotDOM: true, - initialSnapshot: { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - } as Renders, - }); - const { ...rest } = render(); - return { ...rest, ProfiledApp, data, query, client }; -} - -type RenderSuspenseHookOptions = Omit< - RenderHookOptions, - "wrapper" -> & { - client?: ApolloClient; - link?: ApolloLink; - cache?: ApolloCache; - mocks?: MockedResponse[]; - strictMode?: boolean; -}; - -interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: Result[]; -} + renderWithClient(, { client, wrapper: Profiler }); -interface SimpleQueryData { - greeting: string; -} + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); -function renderSuspenseHook( - render: (initialProps: Props) => Result, - options: RenderSuspenseHookOptions = Object.create(null) -) { - function SuspenseFallback() { - renders.suspenseCount++; + { + const { renderedComponents } = await Profiler.takeRender(); - return
loading
; + expect(renderedComponents).toStrictEqual([App]); } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; - - const { mocks = [], strictMode, ...renderHookOptions } = options; + // Wait long enough for auto dispose to kick in + await wait(50); - const client = - options.client || - new ApolloClient({ - cache: options.cache || new InMemoryCache(), - link: options.link || new MockLink(mocks), - }); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); - const view = renderHook( - (props) => { - renders.count++; + await act(() => user.click(screen.getByText("Toggle"))); - const view = render(props); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - renders.frames.push(view); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - return view; - }, - { - ...renderHookOptions, - wrapper: ({ children }) => { - const Wrapper = strictMode ? StrictMode : Fragment; - - return ( - - }> - Error} - onError={(error) => { - renders.errorCount++; - renders.errors.push(error); - }} - > - {children} - - - - ); - }, - } - ); + client.writeQuery({ + query, + data: { greeting: "Hello again" }, + }); - return { ...view, renders }; -} + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); -describe("useBackgroundQuery", () => { - it("fetches a simple query with minimal config", async () => { - const query = gql` - query { - hello - } - `; - const mocks = [ - { - request: { query }, - result: { data: { hello: "world 1" } }, - }, - ]; - const { result } = renderHook(() => useBackgroundQuery(query), { - wrapper: ({ children }) => ( - {children} - ), + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const [queryRef] = result.current; - - const _result = await unwrapQueryRef(queryRef).promise; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - expect(_result).toEqual({ - data: { hello: "world 1" }, - loading: false, - networkStatus: 7, - }); +it("auto resubscribes when mounting useReadQuery after naturally disposed by useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), }); - it("allows the client to be overridden", async () => { - const query: TypedDocumentNode = gql` - query UserQuery { - greeting - } - `; - - const globalClient = new ApolloClient({ - link: new ApolloLink(() => - Observable.of({ data: { greeting: "global hello" } }) - ), - cache: new InMemoryCache(), - }); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const localClient = new ApolloClient({ - link: new ApolloLink(() => - Observable.of({ data: { greeting: "local hello" } }) - ), - cache: new InMemoryCache(), - }); + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + const [queryRef] = useBackgroundQuery(query); - const { result } = renderSuspenseHook( - () => useBackgroundQuery(query, { client: localClient }), - { client: globalClient } + return ( + <> + + }> + {show && } + + ); + } - const [queryRef] = result.current; + renderWithClient(, { client, wrapper: Profiler }); - const _result = await unwrapQueryRef(queryRef).promise; + const toggleButton = screen.getByText("Toggle"); - await waitFor(() => { - expect(_result).toEqual({ - data: { greeting: "local hello" }, - loading: false, - networkStatus: NetworkStatus.ready, - }); - }); - }); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); - it("passes context to the link", async () => { - const query = gql` - query ContextQuery { - context - } - `; + { + const { renderedComponents } = await Profiler.takeRender(); - const link = new ApolloLink((operation) => { - return new Observable((observer) => { - const { valueA, valueB } = operation.getContext(); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - observer.next({ data: { context: { valueA, valueB } } }); - observer.complete(); - }); + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const { result } = renderHook( - () => - useBackgroundQuery(query, { - context: { valueA: "A", valueB: "B" }, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); - const [queryRef] = result.current; + expect(client.getObservableQueries().size).toBe(0); + // We retain the cache entry in useBackgroundQuery to avoid recreating the + // queryRef if useBackgroundQuery rerenders before useReadQuery is mounted + // again. + expect(client).toHaveSuspenseCacheEntryUsing(query); - const _result = await unwrapQueryRef(queryRef).promise; + await act(() => user.click(toggleButton)); - await waitFor(() => { - expect(_result).toMatchObject({ - data: { context: { valueA: "A", valueB: "B" } }, - networkStatus: NetworkStatus.ready, - }); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } + + client.writeQuery({ + query, + data: { greeting: "Hello again" }, }); - it('enables canonical results when canonizeResults is "true"', async () => { - interface Result { - __typename: string; - value: number; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const cache = new InMemoryCache({ - typePolicies: { - Result: { - keyFields: false, - }, - }, + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const query: TypedDocumentNode<{ results: Result[] }> = gql` - query { - results { - value - } - } - `; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const results: Result[] = [ - { __typename: "Result", value: 0 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 2 }, - { __typename: "Result", value: 3 }, - { __typename: "Result", value: 5 }, - ]; +it("does not recreate queryRef and execute a network request when rerendering useBackgroundQuery after queryRef is disposed", async () => { + const { query } = setupSimpleCase(); + const user = userEvent.setup(); + let fetchCount = 0; + const client = new ApolloClient({ + link: new ApolloLink(() => { + fetchCount++; - cache.writeQuery({ - query, - data: { results }, - }); + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: "Hello" } }); + observer.complete(); + }, 20); + }); + }), + cache: new InMemoryCache(), + }); - const { result } = renderHook( - () => - useBackgroundQuery(query, { - canonizeResults: true, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + // Use a fetchPolicy of no-cache to ensure we can more easily track if + // another network request was made + const [queryRef] = useBackgroundQuery(query, { fetchPolicy: "no-cache" }); + + return ( + <> + + }> + {show && } + + ); + } - const [queryRef] = result.current; + const { rerender } = renderWithClient(, { client, wrapper: Profiler }); - const _result = await unwrapQueryRef(queryRef).promise; - const resultSet = new Set(_result.data.results); - const values = Array.from(resultSet).map((item) => item.value); + const toggleButton = screen.getByText("Toggle"); - expect(_result.data).toEqual({ results }); - expect(_result.data.results.length).toBe(6); - expect(resultSet.size).toBe(5); - expect(values).toEqual([0, 1, 2, 3, 5]); - }); + { + const { renderedComponents } = await Profiler.takeRender(); - it("can disable canonical results when the cache's canonizeResults setting is true", async () => { - interface Result { - __typename: string; - value: number; - } + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - const cache = new InMemoryCache({ - canonizeResults: true, - typePolicies: { - Result: { - keyFields: false, - }, - }, + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const query: TypedDocumentNode<{ results: Result[] }> = gql` - query { - results { - value - } - } - `; + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); - const results: Result[] = [ - { __typename: "Result", value: 0 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 2 }, - { __typename: "Result", value: 3 }, - { __typename: "Result", value: 5 }, - ]; + rerender(); + await Profiler.takeRender(); - cache.writeQuery({ - query, - data: { results }, + expect(fetchCount).toBe(1); + + await act(() => user.click(toggleButton)); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const { result } = renderHook( - () => - useBackgroundQuery(query, { - canonizeResults: false, - }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); + expect(fetchCount).toBe(1); - const [queryRef] = result.current; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const _result = await unwrapQueryRef(queryRef).promise; - const resultSet = new Set(_result.data.results); - const values = Array.from(resultSet).map((item) => item.value); +// https://github.com/apollographql/apollo-client/issues/11815 +it("does not recreate queryRef or execute a network request when rerendering useBackgroundQuery in strict mode", async () => { + const { query } = setupSimpleCase(); + const user = userEvent.setup(); + let fetchCount = 0; + const client = new ApolloClient({ + link: new ApolloLink(() => { + fetchCount++; - expect(_result.data).toEqual({ results }); - expect(_result.data.results.length).toBe(6); - expect(resultSet.size).toBe(6); - expect(values).toEqual([0, 1, 1, 2, 3, 5]); + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: "Hello" } }); + observer.complete(); + }, 20); + }); + }), + cache: new InMemoryCache(), }); - // TODO(FIXME): test fails, should return cache data first if it exists - it.skip("returns initial cache data followed by network data when the fetch policy is `cache-and-network`", async () => { - const query = gql` - { - hello - } - `; - const cache = new InMemoryCache(); - const link = mockSingleLink({ - request: { query }, - result: { data: { hello: "from link" } }, - delay: 20, - }); + const Profiler = createProfiler({ + initialSnapshot: { + queryRef: null as QueryRef | null, + result: null as UseReadQueryResult | null, + }, + }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const client = new ApolloClient({ - link, - cache, - }); + function App() { + useTrackRenders(); + const [, setCount] = React.useState(0); + // Use a fetchPolicy of no-cache to ensure we can more easily track if + // another network request was made + const [queryRef] = useBackgroundQuery(query, { fetchPolicy: "no-cache" }); - cache.writeQuery({ query, data: { hello: "from cache" } }); + Profiler.mergeSnapshot({ queryRef }); - const { result } = renderHook( - () => useBackgroundQuery(query, { fetchPolicy: "cache-and-network" }), - { - wrapper: ({ children }) => ( - {children} - ), - } + return ( + <> + + }> + + + ); + } - const [queryRef] = result.current; + renderWithClient(, { + client, + wrapper: ({ children }) => ( + + {children} + + ), + }); - const _result = await unwrapQueryRef(queryRef).promise; + const incrementButton = screen.getByText("Increment"); - expect(_result).toEqual({ - data: { hello: "from link" }, - loading: false, - networkStatus: 7, - }); - }); + { + const { renderedComponents } = await Profiler.takeRender(); - it("all data is present in the cache, no network request is made", async () => { - const query = gql` - { - hello - } - `; - const cache = new InMemoryCache(); - const link = mockSingleLink({ - request: { query }, - result: { data: { hello: "from link" } }, - delay: 20, - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - const client = new ApolloClient({ - link, - cache, - }); + // eslint-disable-next-line testing-library/render-result-naming-convention + const firstRender = await Profiler.takeRender(); + const initialQueryRef = firstRender.snapshot.queryRef; - cache.writeQuery({ query, data: { hello: "from cache" } }); + await act(() => user.click(incrementButton)); - const { result } = renderHook( - () => useBackgroundQuery(query, { fetchPolicy: "cache-first" }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); + { + const { snapshot } = await Profiler.takeRender(); - const [queryRef] = result.current; + expect(snapshot.queryRef).toBe(initialQueryRef); + expect(fetchCount).toBe(1); + } - const _result = await unwrapQueryRef(queryRef).promise; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - expect(_result).toEqual({ - data: { hello: "from cache" }, - loading: false, - networkStatus: 7, - }); +it("disposes of the queryRef when unmounting before it is used by useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), }); - it("partial data is present in the cache so it is ignored and network request is made", async () => { - const query = gql` - { - hello - foo - } - `; - const cache = new InMemoryCache(); - const link = mockSingleLink({ - request: { query }, - result: { data: { hello: "from link", foo: "bar" } }, - delay: 20, - }); - const client = new ApolloClient({ - link, - cache, - }); + const Profiler = createDefaultProfiler(); - // we expect a "Missing field 'foo' while writing result..." error - // when writing hello to the cache, so we'll silence the console.error - const originalConsoleError = console.error; - console.error = () => { - /* noop */ - }; - cache.writeQuery({ query, data: { hello: "from cache" } }); - console.error = originalConsoleError; + function App() { + useTrackRenders(); + useBackgroundQuery(query); - const { result } = renderHook( - () => useBackgroundQuery(query, { fetchPolicy: "cache-first" }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); + return null; + } - const [queryRef] = result.current; + const { unmount } = renderWithClient(, { client, wrapper: Profiler }); - const _result = await unwrapQueryRef(queryRef).promise; + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); - expect(_result).toEqual({ - data: { foo: "bar", hello: "from link" }, - loading: false, - networkStatus: 7, - }); - }); + { + const { renderedComponents } = await Profiler.takeRender(); - it("existing data in the cache is ignored", async () => { - const query = gql` - { - hello - } - `; - const cache = new InMemoryCache(); - const link = mockSingleLink({ - request: { query }, - result: { data: { hello: "from link" } }, - delay: 20, - }); + expect(renderedComponents).toStrictEqual([App]); + } - const client = new ApolloClient({ - link, - cache, - }); + unmount(); + await wait(0); - cache.writeQuery({ query, data: { hello: "from cache" } }); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); - const { result } = renderHook( - () => useBackgroundQuery(query, { fetchPolicy: "network-only" }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); +it("disposes of old queryRefs when changing variables before the queryRef is used by useReadQuery", async () => { + const { query, mocks } = setupVariablesCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); - const [queryRef] = result.current; + const Profiler = createDefaultProfiler(); - const _result = await unwrapQueryRef(queryRef).promise; + function App({ id }: { id: string }) { + useTrackRenders(); + useBackgroundQuery(query, { variables: { id } }); - expect(_result).toEqual({ - data: { hello: "from link" }, - loading: false, - networkStatus: 7, - }); - expect(client.cache.extract()).toEqual({ - ROOT_QUERY: { __typename: "Query", hello: "from link" }, - }); + return null; + } + + const { rerender } = renderWithClient(, { + client, + wrapper: Profiler, }); - it("fetches data from the network but does not update the cache", async () => { - const query = gql` - { - hello - } - `; - const cache = new InMemoryCache(); - const link = mockSingleLink({ - request: { query }, - result: { data: { hello: "from link" } }, - delay: 20, - }); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query, { + variables: { id: "1" }, + }); - const client = new ApolloClient({ - link, - cache, - }); + { + const { renderedComponents } = await Profiler.takeRender(); - cache.writeQuery({ query, data: { hello: "from cache" } }); + expect(renderedComponents).toStrictEqual([App]); + } - const { result } = renderHook( - () => useBackgroundQuery(query, { fetchPolicy: "no-cache" }), - { - wrapper: ({ children }) => ( - {children} - ), - } - ); + rerender(); - const [queryRef] = result.current; + await wait(0); - const _result = await unwrapQueryRef(queryRef).promise; + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query, { + variables: { id: "2" }, + }); + expect(client).not.toHaveSuspenseCacheEntryUsing(query, { + variables: { id: "1" }, + }); +}); - expect(_result).toEqual({ - data: { hello: "from link" }, - loading: false, - networkStatus: 7, - }); - // ...but not updated in the cache - expect(client.cache.extract()).toEqual({ - ROOT_QUERY: { __typename: "Query", hello: "from cache" }, - }); +it("does not prematurely dispose of the queryRef when using strict mode", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), }); - describe("integration tests with useReadQuery", () => { - it("suspends and renders hello", async () => { - const { renders } = renderIntegrationTest(); - // ensure the hook suspends immediately - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); + const Profiler = createDefaultProfiler(); - // the parent component re-renders when promise fulfilled - expect(await screen.findByText("hello")).toBeInTheDocument(); - expect(renders.count).toBe(1); - }); + function App() { + useTrackRenders(); + useBackgroundQuery(query); - it("works with startTransition to change variables", async () => { - type Variables = { - id: string; - }; + return null; + } - interface Data { - todo: { - id: string; - name: string; - completed: boolean; - }; - } - const user = userEvent.setup(); + renderWithClient(, { + client, + wrapper: ({ children }) => ( + + {children} + + ), + }); - const query: TypedDocumentNode = gql` - query TodoItemQuery($id: ID!) { - todo(id: $id) { - id - name - completed - } - } - `; + { + const { renderedComponents } = await Profiler.takeRender(); - const mocks: MockedResponse[] = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { todo: { id: "1", name: "Clean room", completed: false } }, - }, - delay: 10, - }, - { - request: { query, variables: { id: "2" } }, - result: { - data: { - todo: { id: "2", name: "Take out trash", completed: true }, - }, - }, - delay: 10, - }, - ]; + expect(renderedComponents).toStrictEqual([App]); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + await wait(10); - function App() { - return ( - - }> - - - - ); - } + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); +}); - function SuspenseFallback() { - return

Loading

; - } +it("disposes of the queryRef when unmounting before it is used by useReadQuery even if it has been rerendered", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); + const user = userEvent.setup(); - function Parent() { - const [id, setId] = React.useState("1"); - const [queryRef] = useBackgroundQuery(query, { - variables: { id }, - }); - return ; - } + const Profiler = createDefaultProfiler(); - function Todo({ - queryRef, - onChange, - }: { - queryRef: QueryReference; - onChange: (id: string) => void; - }) { - const { data } = useReadQuery(queryRef); - const [isPending, startTransition] = React.useTransition(); - const { todo } = data; - - return ( - <> - -
- {todo.name} - {todo.completed && " (completed)"} -
- - ); - } + function App() { + useTrackRenders(); + useBackgroundQuery(query); - render(); + const [a, setA] = React.useState(0); - expect(screen.getByText("Loading")).toBeInTheDocument(); + return ( + <> + + + ); + } - expect(await screen.findByTestId("todo")).toBeInTheDocument(); + const { unmount } = renderWithClient(, { + client, + wrapper: Profiler, + }); + const button = screen.getByText("Increment"); - const todo = screen.getByTestId("todo"); - const button = screen.getByText("Refresh"); + await act(() => user.click(button)); - expect(todo).toHaveTextContent("Clean room"); + { + const { renderedComponents } = await Profiler.takeRender(); - await act(() => user.click(button)); + expect(renderedComponents).toStrictEqual([App]); + } - // startTransition will avoid rendering the suspense fallback for already - // revealed content if the state update inside the transition causes the - // component to suspend. - // - // Here we should not see the suspense fallback while the component suspends - // until the todo is finished loading. Seeing the suspense fallback is an - // indication that we are suspending the component too late in the process. - expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); - // We can ensure this works with isPending from useTransition in the process - expect(todo).toHaveAttribute("aria-busy", "true"); + await wait(0); - // Ensure we are showing the stale UI until the new todo has loaded - expect(todo).toHaveTextContent("Clean room"); + unmount(); + await wait(0); + expect(client.getObservableQueries().size).toBe(0); +}); - // Eventually we should see the updated todo content once its done - // suspending. - await waitFor(() => { - expect(todo).toHaveTextContent("Take out trash (completed)"); - }); - }); +it("allows the client to be overridden", async () => { + const { query } = setupSimpleCase(); - it('does not suspend deferred queries with data in the cache and using a "cache-and-network" fetch policy', async () => { - interface Data { - greeting: { - __typename: string; - message: string; - recipient: { name: string; __typename: string }; - }; - } + const globalClient = new ApolloClient({ + link: new ApolloLink(() => + Observable.of({ data: { greeting: "global hello" } }) + ), + cache: new InMemoryCache(), + }); - const query: TypedDocumentNode = gql` - query { - greeting { - message - ... on Greeting @defer { - recipient { - name - } - } - } - } - `; + const localClient = new ApolloClient({ + link: new ApolloLink(() => + Observable.of({ data: { greeting: "local hello" } }) + ), + cache: new InMemoryCache(), + }); - const link = new MockSubscriptionLink(); - const cache = new InMemoryCache(); - cache.writeQuery({ - query, - data: { - greeting: { - __typename: "Greeting", - message: "Hello cached", - recipient: { __typename: "Person", name: "Cached Alice" }, - }, - }, - }); - const client = new ApolloClient({ cache, link }); - let renders = 0; - let suspenseCount = 0; - - function App() { - return ( - - }> - - - - ); - } + const Profiler = createDefaultProfiler(); - function SuspenseFallback() { - suspenseCount++; - return

Loading

; - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Parent() { - const [queryRef] = useBackgroundQuery(query, { - fetchPolicy: "cache-and-network", - }); - return ; - } + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { client: localClient }); - function Todo({ queryRef }: { queryRef: QueryReference }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - const { greeting } = data; - renders++; - - return ( - <> -
Message: {greeting.message}
-
Recipient: {greeting.recipient.name}
-
Network status: {networkStatus}
-
Error: {error ? error.message : "none"}
- - ); - } + return ( + }> + + + ); + } - render(); + renderWithClient(, { client: globalClient, wrapper: Profiler }); - expect(screen.getByText(/Message/i)).toHaveTextContent( - "Message: Hello cached" - ); - expect(screen.getByText(/Recipient/i)).toHaveTextContent( - "Recipient: Cached Alice" - ); - expect(screen.getByText(/Network status/i)).toHaveTextContent( - "Network status: 1" // loading - ); - expect(screen.getByText(/Error/i)).toHaveTextContent("none"); + { + const { renderedComponents } = await Profiler.takeRender(); - link.simulateResult({ - result: { - data: { - greeting: { __typename: "Greeting", message: "Hello world" }, - }, - hasNext: true, - }, - }); - - await waitFor(() => { - expect(screen.getByText(/Message/i)).toHaveTextContent( - "Message: Hello world" - ); - }); - expect(screen.getByText(/Recipient/i)).toHaveTextContent( - "Recipient: Cached Alice" - ); - expect(screen.getByText(/Network status/i)).toHaveTextContent( - "Network status: 7" // ready - ); - expect(screen.getByText(/Error/i)).toHaveTextContent("none"); - - link.simulateResult({ - result: { - incremental: [ - { - data: { - recipient: { name: "Alice", __typename: "Person" }, - __typename: "Greeting", - }, - path: ["greeting"], - }, - ], - hasNext: false, - }, - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - await waitFor(() => { - expect(screen.getByText(/Recipient/i)).toHaveTextContent( - "Recipient: Alice" - ); - }); - expect(screen.getByText(/Message/i)).toHaveTextContent( - "Message: Hello world" - ); - expect(screen.getByText(/Network status/i)).toHaveTextContent( - "Network status: 7" // ready - ); - expect(screen.getByText(/Error/i)).toHaveTextContent("none"); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(renders).toBe(3); - expect(suspenseCount).toBe(0); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "local hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - }); + } - it("reacts to cache updates", async () => { - const { renders, client, query } = renderIntegrationTest(); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); +it("passes context to the link", async () => { + const query = gql` + query ContextQuery { + context + } + `; - // the parent component re-renders when promise fulfilled - expect(await screen.findByText("hello")).toBeInTheDocument(); - expect(renders.count).toBe(1); + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + const { valueA, valueB } = operation.getContext(); - client.writeQuery({ - query, - data: { foo: { bar: "baz" } }, + observer.next({ data: { context: { valueA, valueB } } }); + observer.complete(); }); + }); - // the parent component re-renders when promise fulfilled - expect(await screen.findByText("baz")).toBeInTheDocument(); + const Profiler = createDefaultProfiler(); - expect(renders.count).toBe(2); - expect(renders.suspenseCount).toBe(1); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - client.writeQuery({ - query, - data: { foo: { bar: "bat" } }, + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + context: { valueA: "A", valueB: "B" }, }); - expect(await screen.findByText("bat")).toBeInTheDocument(); + return ( + }> + + + ); + } + + renderWithMocks(, { link, wrapper: Profiler }); - expect(renders.suspenseCount).toBe(1); - }); + { + const { renderedComponents } = await Profiler.takeRender(); - it("reacts to variables updates", async () => { - const { renders, rerender } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); - expect(await screen.findByText("1 - Spider-Man")).toBeInTheDocument(); + expect(snapshot.result).toEqual({ + data: { context: { valueA: "A", valueB: "B" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); - rerender({ variables: { id: "2" } }); +it('enables canonical results when canonizeResults is "true"', async () => { + interface Result { + __typename: string; + value: number; + } - expect(renders.suspenseCount).toBe(2); - expect(screen.getByText("loading")).toBeInTheDocument(); + interface Data { + results: Result[]; + } - expect(await screen.findByText("2 - Black Widow")).toBeInTheDocument(); + const cache = new InMemoryCache({ + typePolicies: { + Result: { + keyFields: false, + }, + }, }); - it("does not suspend when `skip` is true", async () => { - interface Data { - greeting: string; + const query: TypedDocumentNode = gql` + query { + results { + value + } } + `; - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + cache.writeQuery({ query, data: { results } }); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + const Profiler = createDefaultProfiler(); - function SuspenseFallback() { - return
Loading...
; - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Parent() { - const [queryRef] = useBackgroundQuery(query, { skip: true }); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { canonizeResults: true }); - return ( - }> - {queryRef && } - - ); - } + return ( + }> + + + ); + } - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + renderWithMocks(, { cache, wrapper: Profiler }); - return
{data.greeting}
; - } + const { + snapshot: { result }, + } = await Profiler.takeRender(); - function App() { - return ( - - - - ); - } + const resultSet = new Set(result!.data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(result!.data).toEqual({ results }); + expect(result!.data.results.length).toBe(6); + expect(resultSet.size).toBe(5); + expect(values).toEqual([0, 1, 2, 3, 5]); +}); - render(); +it("can disable canonical results when the cache's canonizeResults setting is true", async () => { + interface Result { + __typename: string; + value: number; + } + + interface Data { + results: Result[]; + } - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); + const cache = new InMemoryCache({ + canonizeResults: true, + typePolicies: { + Result: { + keyFields: false, + }, + }, }); - it("does not suspend when using `skipToken` in options", async () => { - interface Data { - greeting: string; + const query: TypedDocumentNode = gql` + query { + results { + value + } } + `; - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + cache.writeQuery({ query, data: { results } }); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + const Profiler = createDefaultProfiler(); - function SuspenseFallback() { - return
Loading...
; - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Parent() { - const [queryRef] = useBackgroundQuery(query, skipToken); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { canonizeResults: false }); - return ( - }> - {queryRef && } - - ); - } + return ( + }> + + + ); + } - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + renderWithMocks(, { cache, wrapper: Profiler }); - return
{data.greeting}
; - } + const { snapshot } = await Profiler.takeRender(); + const result = snapshot.result!; - function App() { - return ( - - - - ); - } + const resultSet = new Set(result.data.results); + const values = Array.from(resultSet).map((item) => item.value); - render(); + expect(result.data).toEqual({ results }); + expect(result.data.results.length).toBe(6); + expect(resultSet.size).toBe(6); + expect(values).toEqual([0, 1, 1, 2, 3, 5]); +}); - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); +it("returns initial cache data followed by network data when the fetch policy is `cache-and-network`", async () => { + const { query } = setupSimpleCase(); + const cache = new InMemoryCache(); + const link = mockSingleLink({ + request: { query }, + result: { data: { greeting: "from link" } }, + delay: 20, }); - it("suspends when `skip` becomes `false` after it was `true`", async () => { - interface Data { - greeting: string; - } + const client = new ApolloClient({ link, cache }); - const user = userEvent.setup(); + cache.writeQuery({ query, data: { greeting: "from cache" } }); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const Profiler = createDefaultProfiler(); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-and-network", }); - function SuspenseFallback() { - return
Loading...
; - } - - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery(query, { skip }); - - return ( - <> - - }> - {queryRef && } - - - ); - } + return ( + }> + + + ); + } - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + renderWithClient(, { client, wrapper: Profiler }); - return
{data.greeting}
; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - function App() { - return ( - - - - ); - } + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "from cache" }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } - render(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - await act(() => user.click(screen.getByText("Run query"))); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - expect(screen.getByText("Loading...")).toBeInTheDocument(); +it("all data is present in the cache, no network request is made", async () => { + const { query } = setupSimpleCase(); + const cache = new InMemoryCache(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + let fetchCount = 0; + const link = new ApolloLink((operation) => { + fetchCount++; + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: "from link" } }); + observer.complete(); + }, 20); }); }); - it("suspends when switching away from `skipToken` in options", async () => { - interface Data { - greeting: string; - } + const client = new ApolloClient({ link, cache }); - const user = userEvent.setup(); + cache.writeQuery({ query, data: { greeting: "from cache" } }); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const Profiler = createDefaultProfiler(); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-first", }); - function SuspenseFallback() { - return
Loading...
; - } + return ( + }> + + + ); + } - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery( - query, - skip ? skipToken : undefined - ); + renderWithClient(, { client, wrapper: Profiler }); - return ( - <> - - }> - {queryRef && } - - - ); - } + const { snapshot, renderedComponents } = await Profiler.takeRender(); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "from cache" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); - return
{data.greeting}
; - } + expect(fetchCount).toBe(0); - function App() { - return ( - - - - ); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it("partial data is present in the cache so it is ignored and network request is made", async () => { + const query = gql` + { + hello + foo } + `; + const cache = new InMemoryCache(); + const link = mockSingleLink({ + request: { query }, + result: { data: { hello: "from link", foo: "bar" } }, + delay: 20, + }); - render(); + const client = new ApolloClient({ link, cache }); - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); + { + // we expect a "Missing field 'foo' while writing result..." error + // when writing hello to the cache, so we'll silence the console.error + using _consoleSpy = spyOnConsole("error"); + cache.writeQuery({ query, data: { hello: "from cache" } }); + } - await act(() => user.click(screen.getByText("Run query"))); + const Profiler = createDefaultProfiler(); - expect(screen.getByText("Loading...")).toBeInTheDocument(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-first", }); - }); - it("renders skip result, does not suspend, and maintains `data` when `skip` becomes `true` after it was `false`", async () => { - interface Data { - greeting: string; - } + return ( + }> + + + ); + } - const user = userEvent.setup(); + renderWithClient(, { client, wrapper: Profiler }); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + { + const { renderedComponents } = await Profiler.takeRender(); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { foo: "bar", hello: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function SuspenseFallback() { - return
Loading...
; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function Parent() { - const [skip, setSkip] = React.useState(false); - const [queryRef] = useBackgroundQuery(query, { skip }); +it("existing data in the cache is ignored when fetchPolicy is 'network-only'", async () => { + const { query } = setupSimpleCase(); + const cache = new InMemoryCache(); + const link = mockSingleLink({ + request: { query }, + result: { data: { greeting: "from link" } }, + delay: 20, + }); - return ( - <> - - }> - {queryRef && } - - - ); - } + const client = new ApolloClient({ link, cache }); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + cache.writeQuery({ query, data: { greeting: "from cache" } }); - return
{data.greeting}
; - } + const Profiler = createDefaultProfiler(); - function App() { - return ( - - - - ); - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - render(); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "network-only", + }); - expect(screen.getByText("Loading...")).toBeInTheDocument(); + return ( + }> + + + ); + } - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); - }); + renderWithClient(, { client, wrapper: Profiler }); - await act(() => user.click(screen.getByText("Toggle skip"))); + { + const { renderedComponents } = await Profiler.takeRender(); - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - it("renders skip result, does not suspend, and maintains `data` when switching back to `skipToken`", async () => { - interface Data { - greeting: string; - } + { + const { snapshot } = await Profiler.takeRender(); - const user = userEvent.setup(); + expect(snapshot.result).toEqual({ + data: { greeting: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + expect(client.cache.extract()).toEqual({ + ROOT_QUERY: { __typename: "Query", greeting: "from link" }, + }); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); +it("fetches data from the network but does not update the cache when fetchPolicy is 'no-cache'", async () => { + const { query } = setupSimpleCase(); + const cache = new InMemoryCache(); + const link = mockSingleLink({ + request: { query }, + result: { data: { greeting: "from link" } }, + delay: 20, + }); - function SuspenseFallback() { - return
Loading...
; - } + const client = new ApolloClient({ link, cache }); - function Parent() { - const [skip, setSkip] = React.useState(false); - const [queryRef] = useBackgroundQuery( - query, - skip ? skipToken : undefined - ); + cache.writeQuery({ query, data: { greeting: "from cache" } }); - return ( - <> - - }> - {queryRef && } - - - ); - } + const Profiler = createDefaultProfiler(); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - return
{data.greeting}
; - } + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { fetchPolicy: "no-cache" }); - function App() { - return ( - - - - ); - } + return ( + }> + + + ); + } - render(); + renderWithClient(, { client, wrapper: Profiler }); - expect(screen.getByText("Loading...")).toBeInTheDocument(); + { + const { renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - await act(() => user.click(screen.getByText("Toggle skip"))); + { + const { snapshot } = await Profiler.takeRender(); - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + expect(snapshot.result).toEqual({ + data: { greeting: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(client.cache.extract()).toEqual({ + ROOT_QUERY: { __typename: "Query", greeting: "from cache" }, }); - it("does not make network requests when `skip` is `true`", async () => { - interface Data { - greeting: string; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const user = userEvent.setup(); +it("works with startTransition to change variables", async () => { + type Variables = { + id: string; + }; - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + const user = userEvent.setup(); - let fetchCount = 0; + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; - const link = new ApolloLink((operation) => { - return new Observable((observer) => { - fetchCount++; + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "2" } }, + result: { + data: { + todo: { id: "2", name: "Take out trash", completed: true }, + }, + }, + delay: 10, + }, + ]; - const mock = mocks.find(({ request }) => - equal(request.query, operation.query) - ); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); - if (!mock) { - throw new Error("Could not find mock for operation"); - } + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); - observer.next(mock.result); - observer.complete(); - }); - }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const client = new ApolloClient({ - link, - cache: new InMemoryCache(), + function App() { + useTrackRenders(); + const [id, setId] = React.useState("1"); + const [isPending, startTransition] = React.useTransition(); + const [queryRef] = useBackgroundQuery(query, { + variables: { id }, }); - function SuspenseFallback() { - return
Loading...
; - } - - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery(query, { skip }); + Profiler.mergeSnapshot({ isPending }); - return ( - <> - + return ( + <> + + }> - {queryRef && } + - - ); - } - - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); - - return
{data.greeting}
; - } - - function App() { - return ( - - - ); - } + + ); + } - render(); + renderWithClient(, { client, wrapper: Profiler }); - expect(fetchCount).toBe(0); + { + const { renderedComponents } = await Profiler.takeRender(); - // Toggle skip to `false` - await act(() => user.click(screen.getByText("Toggle skip"))); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(fetchCount).toBe(1); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, }); + } - // Toggle skip to `true` - await act(() => user.click(screen.getByText("Toggle skip"))); - - expect(fetchCount).toBe(1); - }); - - it("does not make network requests when `skipToken` is used", async () => { - interface Data { - greeting: string; - } + await act(() => user.click(screen.getByText("Change todo"))); - const user = userEvent.setup(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component suspends + // until the todo is finished loading. Seeing the suspense fallback is an + // indication that we are suspending the component too late in the process. - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, }, - ]; - - let fetchCount = 0; - - const link = new ApolloLink((operation) => { - return new Observable((observer) => { - fetchCount++; - - const mock = mocks.find(({ request }) => - equal(request.query, operation.query) - ); - - if (!mock) { - throw new Error("Could not find mock for operation"); - } - - observer.next(mock.result); - observer.complete(); - }); }); + } - const client = new ApolloClient({ - link, - cache: new InMemoryCache(), - }); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - function SuspenseFallback() { - return
Loading...
; - } + // Eventually we should see the updated todo content once its done + // suspending. + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "2", name: "Take out trash", completed: true } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } +}); - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery( - query, - skip ? skipToken : undefined - ); +it('does not suspend deferred queries with data in the cache and using a "cache-and-network" fetch policy', async () => { + interface Data { + greeting: { + __typename: string; + message: string; + recipient: { name: string; __typename: string }; + }; + } - return ( - <> - - }> - {queryRef && } - - - ); + const query: TypedDocumentNode = gql` + query { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } } + `; - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + const link = new MockSubscriptionLink(); + const cache = new InMemoryCache(); + cache.writeQuery({ + query, + data: { + greeting: { + __typename: "Greeting", + message: "Hello cached", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + }); + const client = new ApolloClient({ cache, link }); - return
{data.greeting}
; - } + const Profiler = createDefaultProfiler(); - function App() { - return ( - - - - ); - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - render(); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-and-network", + }); - expect(fetchCount).toBe(0); + return ( + }> + + + ); + } - // Toggle skip to `false` - await act(() => user.click(screen.getByText("Toggle skip"))); + renderWithClient(, { client, wrapper: Profiler }); - expect(fetchCount).toBe(1); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello cached", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.loading, }); + } - // Toggle skip to `true` - await act(() => user.click(screen.getByText("Toggle skip"))); - - expect(fetchCount).toBe(1); + link.simulateResult({ + result: { + data: { + greeting: { __typename: "Greeting", message: "Hello world" }, + }, + hasNext: true, + }, }); - it("`skip` result is referentially stable", async () => { - interface Data { - greeting: string; - } - - interface CurrentResult { - current: Data | undefined; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const user = userEvent.setup(); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const result: CurrentResult = { - current: undefined, - }; + link.simulateResult({ + result: { + incremental: [ + { + data: { + recipient: { name: "Alice", __typename: "Person" }, + __typename: "Greeting", + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Alice" }, + }, }, - ]; - - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function SuspenseFallback() { - return
Loading...
; - } - - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery(query, { skip }); - - return ( - <> - - }> - {queryRef && } - - - ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); +it("reacts to cache updates", async () => { + const { query, mocks } = setupSimpleCase(); - result.current = data; + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); - return
{data.greeting}
; - } + const Profiler = createDefaultProfiler(); - function App() { - return ( - - - - ); - } + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const { rerender } = render(); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); - const skipResult = result.current; + return ( + }> + + + ); + } - rerender(); + renderWithClient(, { client, wrapper: Profiler }); - expect(result.current).toBe(skipResult); + { + const { renderedComponents } = await Profiler.takeRender(); - // Toggle skip to `false` - await act(() => user.click(screen.getByText("Toggle skip"))); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(screen.getByText("Loading...")).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const fetchedResult = result.current; + client.writeQuery({ + query, + data: { greeting: "Hello again" }, + }); - rerender(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(result.current).toBe(fetchedResult); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "You again?" }, }); - it("`skip` result is referentially stable when using `skipToken`", async () => { - interface Data { - greeting: string; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - interface CurrentResult { - current: Data | undefined; - } + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "You again?" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const user = userEvent.setup(); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const result: CurrentResult = { - current: undefined, - }; +it("reacts to variables updates", async () => { + const { query, mocks } = setupVariablesCase(); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const Profiler = createDefaultProfiler(); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - ]; + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + function App({ id }: { id: string }) { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { variables: { id } }); - function SuspenseFallback() { - return
Loading...
; - } + return ( + }> + + + ); + } - function Parent() { - const [skip, setSkip] = React.useState(true); - const [queryRef] = useBackgroundQuery( - query, - skip ? skipToken : undefined - ); + const { rerender } = renderWithMocks(, { + mocks, + wrapper: Profiler, + }); - return ( - <> - - }> - {queryRef && } - - - ); - } + { + const { renderedComponents } = await Profiler.takeRender(); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - result.current = data; + { + const { snapshot } = await Profiler.takeRender(); - return
{data.greeting}
; - } + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - function App() { - return ( - - - - ); - } + rerender(); - const { rerender } = render(); + { + const { renderedComponents } = await Profiler.takeRender(); - const skipResult = result.current; + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - rerender(); + { + const { snapshot } = await Profiler.takeRender(); - expect(result.current).toBe(skipResult); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "2", name: "Black Widow" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - // Toggle skip to `false` - await act(() => user.click(screen.getByText("Toggle skip"))); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - expect(screen.getByText("Loading...")).toBeInTheDocument(); +it("does not suspend when `skip` is true", async () => { + const { query, mocks } = setupSimpleCase(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); - }); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const fetchedResult = result.current; + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { skip: true }); - rerender(); + return ( + }> + {queryRef && } + + ); + } - expect(result.current).toBe(fetchedResult); - }); + renderWithMocks(, { mocks, wrapper: Profiler }); - it("`skip` option works with `startTransition`", async () => { - interface Data { - greeting: string; - } + const { renderedComponents } = await Profiler.takeRender(); - const user = userEvent.setup(); + expect(renderedComponents).toStrictEqual([App]); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - delay: 10, - }, - ]; +it("does not suspend when using `skipToken` in options", async () => { + const { query, mocks } = setupSimpleCase(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function SuspenseFallback() { - return
Loading...
; - } + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, skipToken); - function Parent() { - const [skip, setSkip] = React.useState(true); - const [isPending, startTransition] = React.useTransition(); - const [queryRef] = useBackgroundQuery(query, { skip }); + return ( + }> + {queryRef && } + + ); + } - return ( - <> - - }> - {queryRef && } - - - ); - } + renderWithMocks(, { mocks, wrapper: Profiler }); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + const { renderedComponents } = await Profiler.takeRender(); - return
{data.greeting}
; - } + expect(renderedComponents).toStrictEqual([App]); - function App() { - return ( - - - - ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - render(); +it("suspends when `skip` becomes `false` after it was `true`", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); - const button = screen.getByText("Toggle skip"); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - // Toggle skip to `false` - await act(() => user.click(button)); + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, { skip }); - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(button).toBeDisabled(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); + return ( + <> + + }> + {queryRef && } + + + ); + } - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); - }); - }); + renderWithMocks(, { mocks, wrapper: Profiler }); - it("`skipToken` works with `startTransition`", async () => { - interface Data { - greeting: string; - } + { + const { renderedComponents } = await Profiler.takeRender(); - const user = userEvent.setup(); + expect(renderedComponents).toStrictEqual([App]); + } - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + await act(() => user.click(screen.getByText("Run query"))); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - delay: 10, - }, - ]; + { + const { renderedComponents } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function SuspenseFallback() { - return
Loading...
; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function Parent() { - const [skip, setSkip] = React.useState(true); - const [isPending, startTransition] = React.useTransition(); - const [queryRef] = useBackgroundQuery( - query, - skip ? skipToken : undefined - ); +it("suspends when switching away from `skipToken` in options", async () => { + const { query, mocks } = setupSimpleCase(); - return ( - <> - - }> - {queryRef && } - - - ); - } + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined); - return
{data.greeting}
; - } + return ( + <> + + }> + {queryRef && } + + + ); + } - function App() { - return ( - - - - ); - } + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); - render(); + expect(renderedComponents).toStrictEqual([App]); + } - const button = screen.getByText("Toggle skip"); + await act(() => user.click(screen.getByText("Run query"))); - // Toggle skip to `false` - await act(() => user.click(button)); + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); - expect(button).toBeDisabled(); - expect(screen.queryByTestId("greeting")).not.toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("greeting")).toHaveTextContent("Hello"); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - }); + } - it("applies `errorPolicy` on next fetch when it changes between renders", async () => { - interface Data { - greeting: string; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const user = userEvent.setup(); +it("renders skip result, does not suspend, and maintains `data` when `skip` becomes `true` after it was `false`", async () => { + const { query, mocks } = setupSimpleCase(); - const query: TypedDocumentNode = gql` - query { - greeting - } - `; + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, - }, - { - request: { query }, - result: { - errors: [new GraphQLError("oops")], - }, - }, - ]; + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(false); + const [queryRef] = useBackgroundQuery(query, { skip }); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + return ( + <> + + }> + {queryRef && } + + + ); + } - function SuspenseFallback() { - return
Loading...
; - } + renderWithMocks(, { mocks, wrapper: Profiler }); - function Parent() { - const [errorPolicy, setErrorPolicy] = React.useState("none"); - const [queryRef, { refetch }] = useBackgroundQuery(query, { - errorPolicy, - }); + { + const { renderedComponents } = await Profiler.takeRender(); - return ( - <> - - - }> - - - - ); - } + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - function Greeting({ queryRef }: { queryRef: QueryReference }) { - const { data, error } = useReadQuery(queryRef); + { + const { snapshot } = await Profiler.takeRender(); - return error ? ( -
{error.message}
- ) : ( -
{data.greeting}
- ); - } + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - function App() { - return ( - - Error boundary} - > - - - - ); - } + await act(() => user.click(screen.getByText("Toggle skip"))); - render(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(await screen.findByTestId("greeting")).toHaveTextContent("Hello"); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - await act(() => user.click(screen.getByText("Change error policy"))); - await act(() => user.click(screen.getByText("Refetch greeting"))); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - // Ensure we aren't rendering the error boundary and instead rendering the - // error message in the Greeting component. - expect(await screen.findByTestId("error")).toHaveTextContent("oops"); - }); +it("renders skip result, does not suspend, and maintains `data` when switching back to `skipToken`", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - it("applies `context` on next fetch when it changes between renders", async () => { - interface Data { - context: Record; - } + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(false); + const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined); - const user = userEvent.setup(); + return ( + <> + + }> + {queryRef && } + + + ); + } - const query: TypedDocumentNode = gql` - query { - context - } - `; + renderWithMocks(, { mocks, wrapper: Profiler }); - const link = new ApolloLink((operation) => { - return Observable.of({ - data: { - context: operation.getContext(), - }, - }); + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const client = new ApolloClient({ - link, - cache: new InMemoryCache(), + await act(() => user.click(screen.getByText("Toggle skip"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function SuspenseFallback() { - return
Loading...
; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function Parent() { - const [phase, setPhase] = React.useState("initial"); - const [queryRef, { refetch }] = useBackgroundQuery(query, { - context: { phase }, - }); +it("does not make network requests when `skip` is `true`", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); - return ( - <> - - - }> - - - - ); - } - - function Context({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + let fetchCount = 0; - return
{data.context.phase}
; - } + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; - function App() { - return ( - - - + const mock = mocks.find(({ request }) => + equal(request.query, operation.query) ); - } - render(); - - expect(await screen.findByTestId("context")).toHaveTextContent("initial"); + if (!mock) { + throw new Error("Could not find mock for operation"); + } - await act(() => user.click(screen.getByText("Update context"))); - await act(() => user.click(screen.getByText("Refetch"))); + observer.next((mock as any).result); + observer.complete(); + }); + }); - expect(await screen.findByTestId("context")).toHaveTextContent("rerender"); + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), }); - // NOTE: We only test the `false` -> `true` path here. If the option changes - // from `true` -> `false`, the data has already been canonized, so it has no - // effect on the output. - it("returns canonical results immediately when `canonizeResults` changes from `false` to `true` between renders", async () => { - interface Result { - __typename: string; - value: number; - } + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - interface Data { - results: Result[]; - } + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, { skip }); - const cache = new InMemoryCache({ - typePolicies: { - Result: { - keyFields: false, - }, - }, - }); + return ( + <> + + }> + {queryRef && } + + + ); + } - const query: TypedDocumentNode = gql` - query { - results { - value - } - } - `; + renderWithClient(, { client, wrapper: Profiler }); - const results: Result[] = [ - { __typename: "Result", value: 0 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 1 }, - { __typename: "Result", value: 2 }, - { __typename: "Result", value: 3 }, - { __typename: "Result", value: 5 }, - ]; + // initial skipped result + await Profiler.takeRender(); + expect(fetchCount).toBe(0); - const user = userEvent.setup(); + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); - cache.writeQuery({ - query, - data: { results }, - }); + expect(fetchCount).toBe(1); + { + const { renderedComponents } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink([]), - cache, - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - const result: { current: Data | null } = { - current: null, - }; + { + const { snapshot } = await Profiler.takeRender(); - function SuspenseFallback() { - return
Loading...
; - } + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - function Parent() { - const [canonizeResults, setCanonizeResults] = React.useState(false); - const [queryRef] = useBackgroundQuery(query, { - canonizeResults, - }); + // Toggle skip to `true` + await act(() => user.click(screen.getByText("Toggle skip"))); - return ( - <> - - }> - - - - ); - } + expect(fetchCount).toBe(1); + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); - function Results({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); +it("does not make network requests when `skipToken` is used", async () => { + const { query, mocks } = setupSimpleCase(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const user = userEvent.setup(); - result.current = data; + let fetchCount = 0; - return null; - } + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; - function App() { - return ( - - - + const mock = mocks.find(({ request }) => + equal(request.query, operation.query) ); - } - - render(); - function verifyCanonicalResults(data: Data, canonized: boolean) { - const resultSet = new Set(data.results); - const values = Array.from(resultSet).map((item) => item.value); + if (!mock) { + throw new Error("Could not find mock for operation"); + } - expect(data).toEqual({ results }); + observer.next((mock as any).result); + observer.complete(); + }); + }); - if (canonized) { - expect(data.results.length).toBe(6); - expect(resultSet.size).toBe(5); - expect(values).toEqual([0, 1, 2, 3, 5]); - } else { - expect(data.results.length).toBe(6); - expect(resultSet.size).toBe(6); - expect(values).toEqual([0, 1, 1, 2, 3, 5]); - } - } + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); - verifyCanonicalResults(result.current!, false); + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined); - await act(() => user.click(screen.getByText("Canonize results"))); + return ( + <> + + }> + {queryRef && } + + + ); + } - verifyCanonicalResults(result.current!, true); - }); + renderWithClient(, { client, wrapper: Profiler }); - it("applies changed `refetchWritePolicy` to next fetch when changing between renders", async () => { - interface Data { - primes: number[]; - } + // initial skipped result + await Profiler.takeRender(); + expect(fetchCount).toBe(0); - const user = userEvent.setup(); + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); - const query: TypedDocumentNode = gql` - query GetPrimes($min: number, $max: number) { - primes(min: $min, max: $max) - } - `; + expect(fetchCount).toBe(1); + { + const { renderedComponents } = await Profiler.takeRender(); - const mocks = [ - { - request: { query, variables: { min: 0, max: 12 } }, - result: { data: { primes: [2, 3, 5, 7, 11] } }, - }, - { - request: { query, variables: { min: 12, max: 30 } }, - result: { data: { primes: [13, 17, 19, 23, 29] } }, - delay: 10, - }, - { - request: { query, variables: { min: 30, max: 50 } }, - result: { data: { primes: [31, 37, 41, 43, 47] } }, - delay: 10, - }, - ]; + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - const mergeParams: [number[] | undefined, number[]][] = []; + { + const { snapshot } = await Profiler.takeRender(); - const cache = new InMemoryCache({ - typePolicies: { - Query: { - fields: { - primes: { - keyArgs: false, - merge(existing: number[] | undefined, incoming: number[]) { - mergeParams.push([existing, incoming]); - return existing ? existing.concat(incoming) : incoming; - }, - }, - }, - }, - }, + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, + // Toggle skip to `true` + await act(() => user.click(screen.getByText("Toggle skip"))); + + expect(fetchCount).toBe(1); + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } +}); - function SuspenseFallback() { - return
Loading...
; - } +it("does not make network requests when `skipToken` is used in strict mode", async () => { + const { query, mocks } = setupSimpleCase(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const user = userEvent.setup(); - function Parent() { - const [refetchWritePolicy, setRefetchWritePolicy] = - React.useState("merge"); + let fetchCount = 0; - const [queryRef, { refetch }] = useBackgroundQuery(query, { - refetchWritePolicy, - variables: { min: 0, max: 12 }, - }); + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; - return ( - <> - - - - }> - - - + const mock = mocks.find(({ request }) => + equal(request.query, operation.query) ); - } - function Primes({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + if (!mock) { + throw new Error("Could not find mock for operation"); + } - return {data.primes.join(", ")}; - } + observer.next((mock as any).result); + observer.complete(); + }); + }); - function App() { - return ( - - - - ); - } + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); - render(); + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined); - const primes = await screen.findByTestId("primes"); + return ( + <> + + }> + {queryRef && } + + + ); + } - expect(primes).toHaveTextContent("2, 3, 5, 7, 11"); - expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + renderWithClient(, { + client, + wrapper: ({ children }) => ( + + {children} + + ), + }); - await act(() => user.click(screen.getByText("Refetch next"))); + // initial skipped result + await Profiler.takeRender(); + expect(fetchCount).toBe(0); - await waitFor(() => { - expect(primes).toHaveTextContent("2, 3, 5, 7, 11, 13, 17, 19, 23, 29"); - }); + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); + await Profiler.takeRender(); - expect(mergeParams).toEqual([ - [undefined, [2, 3, 5, 7, 11]], - [ - [2, 3, 5, 7, 11], - [13, 17, 19, 23, 29], - ], - ]); + { + const { snapshot } = await Profiler.takeRender(); - await act(() => - user.click(screen.getByText("Change refetch write policy")) - ); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - await act(() => user.click(screen.getByText("Refetch last"))); + expect(fetchCount).toBe(1); - await waitFor(() => { - expect(primes).toHaveTextContent("31, 37, 41, 43, 47"); - }); + // Toggle skip to `true` + await act(() => user.click(screen.getByText("Toggle skip"))); - expect(mergeParams).toEqual([ - [undefined, [2, 3, 5, 7, 11]], - [ - [2, 3, 5, 7, 11], - [13, 17, 19, 23, 29], - ], - [undefined, [31, 37, 41, 43, 47]], - ]); - }); + { + const { snapshot } = await Profiler.takeRender(); - it("applies `returnPartialData` on next fetch when it changes between renders", async () => { - interface Data { - character: { - __typename: "Character"; - id: string; - name: string; - }; - } + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - interface PartialData { - character: { - __typename: "Character"; - id: string; - }; - } + expect(fetchCount).toBe(1); - const user = userEvent.setup(); + await expect(Profiler).not.toRerender(); +}); - const fullQuery: TypedDocumentNode = gql` - query { - character { - __typename - id - name - } - } - `; +it("does not make network requests when using `skip` option in strict mode", async () => { + const { query, mocks } = setupSimpleCase(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const user = userEvent.setup(); - const partialQuery: TypedDocumentNode = gql` - query { - character { - __typename - id - } - } - `; + let fetchCount = 0; - const mocks = [ - { - request: { query: fullQuery }, - result: { - data: { - character: { - __typename: "Character", - id: "1", - name: "Doctor Strange", - }, - }, - }, - }, - { - request: { query: fullQuery }, - result: { - data: { - character: { - __typename: "Character", - id: "1", - name: "Doctor Strange (refetched)", - }, - }, - }, - delay: 100, - }, - ]; + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; - const cache = new InMemoryCache(); + const mock = mocks.find(({ request }) => + equal(request.query, operation.query) + ); - cache.writeQuery({ - query: partialQuery, - data: { character: { __typename: "Character", id: "1" } }, - }); + if (!mock) { + throw new Error("Could not find mock for operation"); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, + observer.next((mock as any).result); + observer.complete(); }); + }); - function SuspenseFallback() { - return
Loading...
; - } + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); - function Parent() { - const [returnPartialData, setReturnPartialData] = React.useState(false); + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [queryRef] = useBackgroundQuery(query, { skip }); - const [queryRef] = useBackgroundQuery(fullQuery, { - returnPartialData, - }); + return ( + <> + + }> + {queryRef && } + + + ); + } - return ( - <> - - }> - - - - ); - } + renderWithClient(, { + client, + wrapper: ({ children }) => ( + + {children} + + ), + }); - function Character({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); + // initial skipped result + await Profiler.takeRender(); + expect(fetchCount).toBe(0); - return ( - {data.character.name ?? "unknown"} - ); - } + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); + await Profiler.takeRender(); - function App() { - return ( - - - - ); - } + { + const { snapshot } = await Profiler.takeRender(); - render(); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const character = await screen.findByTestId("character"); + expect(fetchCount).toBe(1); - expect(character).toHaveTextContent("Doctor Strange"); + // Toggle skip to `true` + await act(() => user.click(screen.getByText("Toggle skip"))); - await act(() => user.click(screen.getByText("Update partial data"))); + { + const { snapshot } = await Profiler.takeRender(); - cache.modify({ - id: cache.identify({ __typename: "Character", id: "1" }), - fields: { - name: (_, { DELETE }) => DELETE, - }, + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - await waitFor(() => { - expect(character).toHaveTextContent("unknown"); - }); + expect(fetchCount).toBe(1); - await waitFor(() => { - expect(character).toHaveTextContent("Doctor Strange (refetched)"); - }); - }); + await expect(Profiler).not.toRerender(); +}); - it("applies updated `fetchPolicy` on next fetch when it changes between renders", async () => { - interface Data { - character: { - __typename: "Character"; - id: string; - name: string; - }; - } +it("result is referentially stable", async () => { + const { query, mocks } = setupSimpleCase(); - const user = userEvent.setup(); + let result: UseReadQueryResult | null = null; - const query: TypedDocumentNode = gql` - query { - character { - __typename - id - name - } - } - `; + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - const mocks = [ - { - request: { query }, - result: { - data: { - character: { - __typename: "Character", - id: "1", - name: "Doctor Strange", - }, - }, - }, - delay: 10, - }, - ]; + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); + + return ( + }> + + + ); + } - const cache = new InMemoryCache(); + const { rerender } = renderWithMocks(, { mocks, wrapper: Profiler }); - cache.writeQuery({ - query, - data: { - character: { - __typename: "Character", - id: "1", - name: "Doctor Strangecache", - }, - }, - }); + { + const { renderedComponents } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - function SuspenseFallback() { - return
Loading...
; - } + result = snapshot.result; + } - function Parent() { - const [fetchPolicy, setFetchPolicy] = - React.useState("cache-first"); + rerender(); - const [queryRef, { refetch }] = useBackgroundQuery(query, { - fetchPolicy, - }); + { + const { snapshot } = await Profiler.takeRender(); - return ( - <> - - - }> - - - - ); - } + expect(snapshot.result).toBe(result); + } +}); - function Character({ queryRef }: { queryRef: QueryReference }) { - const { data } = useReadQuery(queryRef); +it("`skip` option works with `startTransition`", async () => { + const { query, mocks } = setupSimpleCase(); - return {data.character.name}; - } + const user = userEvent.setup(); + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function App() { - return ( - - - - ); - } + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [isPending, startTransition] = React.useTransition(); + const [queryRef] = useBackgroundQuery(query, { skip }); - render(); + Profiler.mergeSnapshot({ isPending }); - const character = await screen.findByTestId("character"); + return ( + <> + + }> + {queryRef && } + + + ); + } - expect(character).toHaveTextContent("Doctor Strangecache"); + renderWithMocks(, { mocks, wrapper: Profiler }); - await act(() => user.click(screen.getByText("Change fetch policy"))); - await act(() => user.click(screen.getByText("Refetch"))); - await waitFor(() => { - expect(character).toHaveTextContent("Doctor Strange"); - }); + { + const { renderedComponents } = await Profiler.takeRender(); - // Because we switched to a `no-cache` fetch policy, we should not see the - // newly fetched data in the cache after the fetch occurred. - expect(cache.readQuery({ query })).toEqual({ - character: { - __typename: "Character", - id: "1", - name: "Doctor Strangecache", - }, - }); - }); + expect(renderedComponents).toStrictEqual([App]); + } - it("properly handles changing options along with changing `variables`", async () => { - interface Data { - character: { - __typename: "Character"; - id: string; - name: string; - }; - } + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); - const user = userEvent.setup(); - const query: TypedDocumentNode = gql` - query ($id: ID!) { - character(id: $id) { - __typename - id - name - } - } - `; + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - errors: [new GraphQLError("oops")], - }, - delay: 10, - }, - { - request: { query, variables: { id: "2" } }, - result: { - data: { - character: { - __typename: "Character", - id: "2", - name: "Hulk", - }, - }, - }, - delay: 10, - }, - ]; + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot).toEqual({ + isPending: true, + result: null, + }); + } - const cache = new InMemoryCache(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - cache.writeQuery({ - query, - variables: { - id: "1", - }, - data: { - character: { - __typename: "Character", - id: "1", - name: "Doctor Strangecache", - }, + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }, }); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, - }); + await expect(Profiler).not.toRerender(); +}); - function SuspenseFallback() { - return
Loading...
; - } +it("`skipToken` works with `startTransition`", async () => { + const { query, mocks } = setupSimpleCase(); + const user = userEvent.setup(); - function Parent() { - const [id, setId] = React.useState("1"); + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); - const [queryRef, { refetch }] = useBackgroundQuery(query, { - errorPolicy: id === "1" ? "all" : "none", - variables: { id }, - }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - return ( - <> - - - - Error boundary} - > - }> - - - - - ); - } + function App() { + useTrackRenders(); + const [skip, setSkip] = React.useState(true); + const [isPending, startTransition] = React.useTransition(); + const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined); - function Character({ queryRef }: { queryRef: QueryReference }) { - const { data, error } = useReadQuery(queryRef); + Profiler.mergeSnapshot({ isPending }); - return error ? ( -
{error.message}
- ) : ( - {data.character.name} - ); - } + return ( + <> + + }> + {queryRef && } + + + ); + } - function App() { - return ( - - - - ); - } + renderWithMocks(, { mocks, wrapper: Profiler }); - render(); + { + const { renderedComponents } = await Profiler.takeRender(); - const character = await screen.findByTestId("character"); + expect(renderedComponents).toStrictEqual([App]); + } - expect(character).toHaveTextContent("Doctor Strangecache"); + // Toggle skip to `false` + await act(() => user.click(screen.getByText("Toggle skip"))); - await act(() => user.click(screen.getByText("Get second character"))); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(character).toHaveTextContent("Hulk"); + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot).toEqual({ + isPending: true, + result: null, }); + } - await act(() => user.click(screen.getByText("Get first character"))); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(character).toHaveTextContent("Doctor Strangecache"); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, }); + } - await act(() => user.click(screen.getByText("Refetch"))); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - // Ensure we render the inline error instead of the error boundary, which - // tells us the error policy was properly applied. - expect(await screen.findByTestId("error")).toHaveTextContent("oops"); - }); +it("applies `errorPolicy` on next fetch when it changes between renders", async () => { + const { query } = setupSimpleCase(); + const user = userEvent.setup(); - describe("refetch", () => { - it("re-suspends when calling `refetch`", async () => { - const { ProfiledApp } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - }); + const mocks = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + delay: 10, + }, + { + request: { query }, + result: { + errors: [new GraphQLError("oops")], + }, + delay: 10, + }, + ]; - { - const { withinDOM, snapshot } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(1); - expect(withinDOM().getByText("loading")).toBeInTheDocument(); - } + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); - { - const { withinDOM } = await ProfiledApp.takeRender(); - expect(withinDOM().getByText("1 - Spider-Man")).toBeInTheDocument(); - } + function App() { + useTrackRenders(); + const [errorPolicy, setErrorPolicy] = React.useState("none"); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + errorPolicy, + }); + + return ( + <> + + + }> + + + + + + ); + } - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + renderWithMocks(, { mocks, wrapper: Profiler }); - { - // parent component re-suspends - const { snapshot } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(2); - } - { - const { snapshot, withinDOM } = await ProfiledApp.takeRender(); - // @jerelmiller can you please verify that this is still in the spirit of the test? - // This seems to have moved onto the next render - or before the test skipped one. - expect(snapshot.count).toBe(2); - expect( - withinDOM().getByText("1 - Spider-Man (updated)") - ).toBeInTheDocument(); - } + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); - expect(ProfiledApp).not.toRerender(); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - it("re-suspends when calling `refetch` with new variables", async () => { - interface QueryData { - character: { - id: string; - name: string; - }; - } + } - interface QueryVariables { - id: string; - } - const query: TypedDocumentNode = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: "Captain Marvel" } }, - }, - }, - { - request: { query, variables: { id: "2" } }, - result: { - data: { character: { id: "2", name: "Captain America" } }, - }, - }, - ]; + await act(() => user.click(screen.getByText("Change error policy"))); + await Profiler.takeRender(); - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - mocks, - }); + await act(() => user.click(screen.getByText("Refetch greeting"))); + await Profiler.takeRender(); - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(await screen.findByText("1 - Captain Marvel")).toBeInTheDocument(); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + error: null, + result: { + data: { greeting: "Hello" }, + error: new ApolloError({ graphQLErrors: [new GraphQLError("oops")] }), + networkStatus: NetworkStatus.error, + }, + }); + } +}); - const newVariablesRefetchButton = screen.getByText( - "Set variables to id: 2" - ); - const refetchButton = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(newVariablesRefetchButton)); - await act(() => user.click(refetchButton)); +it("applies `context` on next fetch when it changes between renders", async () => { + interface Data { + context: Record; + } - expect( - await screen.findByText("2 - Captain America") - ).toBeInTheDocument(); + const user = userEvent.setup(); - // parent component re-suspends - expect(renders.suspenseCount).toBe(2); - expect(renders.count).toBe(3); + const query: TypedDocumentNode = gql` + query { + context + } + `; - // extra render puts an additional frame into the array - expect(renders.frames).toMatchObject([ - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - { - ...mocks[1].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - ]); + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + setTimeout(() => { + const { phase } = operation.getContext(); + observer.next({ data: { context: { phase } } }); + observer.complete(); + }, 10); }); - it("re-suspends multiple times when calling `refetch` multiple times", async () => { - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - }); - - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); + }); - expect(await screen.findByText("1 - Spider-Man")).toBeInTheDocument(); + const client = new ApolloClient({ link, cache: new InMemoryCache() }); - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - // parent component re-suspends - expect(renders.suspenseCount).toBe(2); - expect(renders.count).toBe(2); + function App() { + useTrackRenders(); + const [phase, setPhase] = React.useState("initial"); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + context: { phase }, + }); - expect( - await screen.findByText("1 - Spider-Man (updated)") - ).toBeInTheDocument(); + return ( + <> + + + }> + + + + ); + } - await act(() => user.click(button)); + renderWithClient(, { client, wrapper: Profiler }); - // parent component re-suspends - expect(renders.suspenseCount).toBe(3); - expect(renders.count).toBe(3); + { + const { renderedComponents } = await Profiler.takeRender(); - expect( - await screen.findByText("1 - Spider-Man (updated again)") - ).toBeInTheDocument(); - }); - it("throws errors when errors are returned after calling `refetch`", async () => { - using _consoleSpy = spyOnConsole("error"); - interface QueryData { - character: { - id: string; - name: string; - }; - } + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - interface QueryVariables { - id: string; - } - const query: TypedDocumentNode = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: "Captain Marvel" } }, - }, - }, - { - request: { query, variables: { id: "1" } }, - result: { - errors: [new GraphQLError("Something went wrong")], - }, - }, - ]; - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - mocks, - }); + { + const { snapshot } = await Profiler.takeRender(); - expect(renders.suspenseCount).toBe(1); - expect(screen.getByText("loading")).toBeInTheDocument(); + expect(snapshot.result).toEqual({ + data: { context: { phase: "initial" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - expect(await screen.findByText("1 - Captain Marvel")).toBeInTheDocument(); + await act(() => user.click(screen.getByText("Update context"))); + await Profiler.takeRender(); - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); - await waitFor(() => { - expect(renders.errorCount).toBe(1); - }); + { + const { snapshot } = await Profiler.takeRender(); - expect(renders.errors).toEqual([ - new ApolloError({ - graphQLErrors: [new GraphQLError("Something went wrong")], - }), - ]); + expect(snapshot.result).toEqual({ + data: { context: { phase: "rerender" } }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - it('ignores errors returned after calling `refetch` when errorPolicy is set to "ignore"', async () => { - interface QueryData { - character: { - id: string; - name: string; - }; - } - - interface QueryVariables { - id: string; - } - const query: TypedDocumentNode = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: "Captain Marvel" } }, - }, - }, - { - request: { query, variables: { id: "1" } }, - result: { - errors: [new GraphQLError("Something went wrong")], - }, - }, - ]; - - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - errorPolicy: "ignore", - mocks, - }); + } +}); - expect(await screen.findByText("1 - Captain Marvel")).toBeInTheDocument(); +// NOTE: We only test the `false` -> `true` path here. If the option changes +// from `true` -> `false`, the data has already been canonized, so it has no +// effect on the output. +it("returns canonical results immediately when `canonizeResults` changes from `false` to `true` between renders", async () => { + interface Result { + __typename: string; + value: number; + } - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + interface Data { + results: Result[]; + } - expect(renders.errorCount).toBe(0); - expect(renders.errors).toEqual([]); - }); - it('returns errors after calling `refetch` when errorPolicy is set to "all"', async () => { - interface QueryData { - character: { - id: string; - name: string; - }; - } + const cache = new InMemoryCache({ + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); - interface QueryVariables { - id: string; + const query: TypedDocumentNode = gql` + query { + results { + value } - const query: TypedDocumentNode = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: "Captain Marvel" } }, - }, - }, - { - request: { query, variables: { id: "1" } }, - result: { - errors: [new GraphQLError("Something went wrong")], - }, - }, - ]; + } + `; - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - errorPolicy: "all", - mocks, - }); + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; - expect(await screen.findByText("1 - Captain Marvel")).toBeInTheDocument(); + const user = userEvent.setup(); - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + cache.writeQuery({ + query, + data: { results }, + }); - expect(renders.errorCount).toBe(0); - expect(renders.errors).toEqual([]); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - expect( - await screen.findByText("Something went wrong") - ).toBeInTheDocument(); + function App() { + useTrackRenders(); + const [canonizeResults, setCanonizeResults] = React.useState(false); + const [queryRef] = useBackgroundQuery(query, { + canonizeResults, }); - it('handles partial data results after calling `refetch` when errorPolicy is set to "all"', async () => { - interface QueryData { - character: { - id: string; - name: string; - }; - } - interface QueryVariables { - id: string; - } - const query: TypedDocumentNode = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name - } - } - `; - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: "Captain Marvel" } }, - }, - }, - { - request: { query, variables: { id: "1" } }, - result: { - data: { character: { id: "1", name: null } }, - errors: [new GraphQLError("Something went wrong")], - }, - }, - ]; + return ( + <> + + }> + + + + ); + } - const { renders } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - errorPolicy: "all", - mocks, - }); + renderWithMocks(, { cache, wrapper: Profiler }); - expect(await screen.findByText("1 - Captain Marvel")).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); + const result = snapshot.result!; + const resultSet = new Set(result.data.results); + const values = Array.from(resultSet).map((item) => item.value); - const button = screen.getByText("Refetch"); - const user = userEvent.setup(); - await act(() => user.click(button)); + expect(result.data).toEqual({ results }); + expect(result.data.results.length).toBe(6); + expect(resultSet.size).toBe(6); + expect(values).toEqual([0, 1, 1, 2, 3, 5]); + } - expect(renders.errorCount).toBe(0); - expect(renders.errors).toEqual([]); + await act(() => user.click(screen.getByText("Canonize results"))); - expect( - await screen.findByText("Something went wrong") - ).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); + const result = snapshot.result!; + const resultSet = new Set(result.data.results); + const values = Array.from(resultSet).map((item) => item.value); - const expectedError = new ApolloError({ - graphQLErrors: [new GraphQLError("Something went wrong")], - }); + expect(result.data).toEqual({ results }); + expect(result.data.results.length).toBe(6); + expect(resultSet.size).toBe(5); + expect(values).toEqual([0, 1, 2, 3, 5]); + } +}); - expect(renders.frames).toMatchObject([ - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - { - data: mocks[1].result.data, - networkStatus: NetworkStatus.error, - error: expectedError, - }, - ]); - }); +it("applies changed `refetchWritePolicy` to next fetch when changing between renders", async () => { + interface Data { + primes: number[]; + } - it("can refetch after error is encountered", async () => { - type Variables = { - id: string; - }; + const user = userEvent.setup(); - interface Data { - todo: { - id: string; - name: string; - completed: boolean; - }; - } - const user = userEvent.setup(); + const query: TypedDocumentNode = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; - const query: TypedDocumentNode = gql` - query TodoItemQuery($id: ID!) { - todo(id: $id) { - id - name - completed - } - } - `; + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + { + request: { query, variables: { min: 30, max: 50 } }, + result: { data: { primes: [31, 37, 41, 43, 47] } }, + delay: 10, + }, + ]; - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: null, - errors: [new GraphQLError("Oops couldn't fetch")], - }, - delay: 10, - }, - { - request: { query, variables: { id: "1" } }, - result: { - data: { todo: { id: "1", name: "Clean room", completed: true } }, + const mergeParams: [number[] | undefined, number[]][] = []; + + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, }, - delay: 10, }, - ]; + }, + }, + }); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - function App() { - return ( - - - - ); - } + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function SuspenseFallback() { - return

Loading

; - } + function App() { + useTrackRenders(); + const [refetchWritePolicy, setRefetchWritePolicy] = + React.useState("merge"); - function Parent() { - const [queryRef, { refetch }] = useBackgroundQuery(query, { - variables: { id: "1" }, - }); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + refetchWritePolicy, + variables: { min: 0, max: 12 }, + }); - return ( - }> - refetch()} - fallbackRender={({ error, resetErrorBoundary }) => ( - <> - -
{error.message}
- - )} - > - -
-
- ); - } + return ( + <> + + + + }> + + + + ); + } - function Todo({ queryRef }: { queryRef: QueryReference }) { - const { - data: { todo }, - } = useReadQuery(queryRef); - - return ( -
- {todo.name} - {todo.completed && " (completed)"} -
- ); - } + renderWithClient(, { client, wrapper: Profiler }); - render(); + // initial suspended render + await Profiler.takeRender(); - // Disable error message shown in the console due to an uncaught error. - // TODO: need to determine why the error message is logged to the console - // as an uncaught error since other tests do not require this. - { - using _consoleSpy = spyOnConsole("error"); + { + const { snapshot } = await Profiler.takeRender(); - expect(screen.getByText("Loading")).toBeInTheDocument(); + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } - expect( - await screen.findByText("Oops couldn't fetch") - ).toBeInTheDocument(); - } + await act(() => user.click(screen.getByText("Refetch next"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + ]); + } - const button = screen.getByText("Retry"); + await act(() => user.click(screen.getByText("Change refetch write policy"))); + await Profiler.takeRender(); - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Refetch last"))); + await Profiler.takeRender(); - expect(screen.getByText("Loading")).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("todo")).toHaveTextContent( - "Clean room (completed)" - ); - }); + expect(snapshot.result).toEqual({ + data: { primes: [31, 37, 41, 43, 47] }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + [undefined, [31, 37, 41, 43, 47]], + ]); + } +}); - it("throws errors on refetch after error is encountered after first fetch with error", async () => { - type Variables = { - id: string; - }; +it("applies `returnPartialData` on next fetch when it changes between renders", async () => { + const { query } = setupVariablesCase(); - interface Data { - todo: { - id: string; - name: string; - completed: boolean; - }; - } - const user = userEvent.setup(); + interface PartialData { + character: { + __typename: "Character"; + id: string; + }; + } - const query: TypedDocumentNode = gql` - query TodoItemQuery($id: ID!) { - todo(id: $id) { - id - name - completed - } - } - `; + const user = userEvent.setup(); - const mocks = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: null, - errors: [new GraphQLError("Oops couldn't fetch")], + const partialQuery: TypedDocumentNode = gql` + query { + character { + __typename + id + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange", }, - delay: 10, }, - { - request: { query, variables: { id: "1" } }, - result: { - data: null, - errors: [new GraphQLError("Oops couldn't fetch again")], + }, + delay: 10, + }, + { + request: { query }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange (refetched)", }, - delay: 10, }, - ]; - - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + }, + delay: 10, + }, + ]; - function App() { - return ( - - - - ); - } + const cache = new InMemoryCache(); - function SuspenseFallback() { - return

Loading

; - } + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + }); - function Parent() { - const [queryRef, { refetch }] = useBackgroundQuery(query, { - variables: { id: "1" }, - }); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - return ( - }> - refetch()} - fallbackRender={({ error, resetErrorBoundary }) => ( - <> - -
{error.message}
- - )} - > - -
-
- ); - } + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Todo({ queryRef }: { queryRef: QueryReference }) { - const { - data: { todo }, - } = useReadQuery(queryRef); - - return ( -
- {todo.name} - {todo.completed && " (completed)"} -
- ); - } + function App() { + useTrackRenders(); + const [returnPartialData, setReturnPartialData] = React.useState(false); + const [queryRef] = useBackgroundQuery(query, { returnPartialData }); - render(); + return ( + <> + + }> + + + + ); + } - // Disable error message shown in the console due to an uncaught error. - // TODO: need to determine why the error message is logged to the console - // as an uncaught error since other tests do not require this. - using _consoleSpy = spyOnConsole("error"); + renderWithClient(, { client, wrapper: Profiler }); - expect(screen.getByText("Loading")).toBeInTheDocument(); + // initial suspended render + await Profiler.takeRender(); - expect( - await screen.findByText("Oops couldn't fetch") - ).toBeInTheDocument(); + { + const { snapshot } = await Profiler.takeRender(); - const button = screen.getByText("Retry"); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Doctor Strange" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Update partial data"))); + await Profiler.takeRender(); - expect(screen.getByText("Loading")).toBeInTheDocument(); + cache.modify({ + id: cache.identify({ __typename: "Character", id: "1" }), + fields: { + name: (_, { DELETE }) => DELETE, + }, + }); - await waitFor(() => { - expect( - screen.getByText("Oops couldn't fetch again") - ).toBeInTheDocument(); - }); + { + const { snapshot } = await Profiler.takeRender(); - expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, }); + } - it("`refetch` works with startTransition to allow React to show stale UI until finished suspending", async () => { - type Variables = { - id: string; - }; + { + const { snapshot } = await Profiler.takeRender(); - interface Data { - todo: { - id: string; - name: string; - completed: boolean; - }; - } - const user = userEvent.setup(); + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange (refetched)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); - const query: TypedDocumentNode = gql` - query TodoItemQuery($id: ID!) { - todo(id: $id) { - id - name - completed - } - } - `; +it("applies updated `fetchPolicy` on next fetch when it changes between renders", async () => { + const { query, mocks } = setupVariablesCase(); - const mocks: MockedResponse[] = [ - { - request: { query, variables: { id: "1" } }, - result: { - data: { todo: { id: "1", name: "Clean room", completed: false } }, - }, - delay: 10, - }, - { - request: { query, variables: { id: "1" } }, - result: { - data: { todo: { id: "1", name: "Clean room", completed: true } }, - }, - delay: 10, - }, - ]; + const user = userEvent.setup(); + const cache = new InMemoryCache(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache(), - }); + cache.writeQuery({ + query, + variables: { id: "1" }, + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Cacheman", + }, + }, + }); - function App() { - return ( - - }> - - - - ); - } + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - function SuspenseFallback() { - return

Loading

; - } + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Parent() { - const [id, setId] = React.useState("1"); - const [queryRef, { refetch }] = useBackgroundQuery(query, { - variables: { id }, - }); - const onRefetchHandler = () => { - refetch(); - }; - return ( - - ); - } + function App() { + const [fetchPolicy, setFetchPolicy] = + React.useState("cache-first"); - function Todo({ - queryRef, - onRefetch, - }: { - onRefetch: () => void; - queryRef: QueryReference; - onChange: (id: string) => void; - }) { - const { data } = useReadQuery(queryRef); - const [isPending, startTransition] = React.useTransition(); - const { todo } = data; - - return ( - <> - -
- {todo.name} - {todo.completed && " (completed)"} -
- - ); - } + const [queryRef, { refetch }] = useBackgroundQuery(query, { + fetchPolicy, + variables: { id: "1" }, + }); - const ProfiledApp = profile({ Component: App, snapshotDOM: true }); + return ( + <> + + + }> + + + + ); + } - render(); + renderWithClient(, { client, wrapper: Profiler }); - { - const { withinDOM } = await ProfiledApp.takeRender(); - expect(withinDOM().getByText("Loading")).toBeInTheDocument(); - } + { + const { snapshot } = await Profiler.takeRender(); - { - const { withinDOM } = await ProfiledApp.takeRender(); - const todo = withinDOM().getByTestId("todo"); - expect(todo).toBeInTheDocument(); - expect(todo).toHaveTextContent("Clean room"); - } + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Cacheman", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const button = screen.getByText("Refresh"); - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Change fetch policy"))); + { + const { snapshot } = await Profiler.takeRender(); - // startTransition will avoid rendering the suspense fallback for already - // revealed content if the state update inside the transition causes the - // component to suspend. - // - // Here we should not see the suspense fallback while the component suspends - // until the todo is finished loading. Seeing the suspense fallback is an - // indication that we are suspending the component too late in the process. - { - const { withinDOM } = await ProfiledApp.takeRender(); - const todo = withinDOM().getByTestId("todo"); - expect(withinDOM().queryByText("Loading")).not.toBeInTheDocument(); + // ensure we haven't changed the result yet just by changing the fetch policy + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Cacheman", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - // We can ensure this works with isPending from useTransition in the process - expect(todo).toHaveAttribute("aria-busy", "true"); + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); - // Ensure we are showing the stale UI until the new todo has loaded - expect(todo).toHaveTextContent("Clean room"); - } + { + const { snapshot } = await Profiler.takeRender(); - // Eventually we should see the updated todo content once its done - // suspending. - { - const { withinDOM } = await ProfiledApp.takeRender(); - const todo = withinDOM().getByTestId("todo"); - expect(todo).toHaveTextContent("Clean room (completed)"); - } + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } + + // Because we switched to a `no-cache` fetch policy, we should not see the + // newly fetched data in the cache after the fetch occurred. + expect(cache.readQuery({ query, variables: { id: "1" } })).toEqual({ + character: { + __typename: "Character", + id: "1", + name: "Spider-Cacheman", + }, }); +}); - describe("fetchMore", () => { - function getItemTexts( - screen: Pick = _screen - ) { - return screen.getAllByTestId(/letter/).map( - // eslint-disable-next-line testing-library/no-node-access - (li) => li.firstChild!.textContent - ); - } +it("properly handles changing options along with changing `variables`", async () => { + const { query } = setupVariablesCase(); + const user = userEvent.setup(); + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("oops")], + }, + delay: 10, + }, + { + request: { query, variables: { id: "2" } }, + result: { + data: { + character: { + __typename: "Character", + id: "2", + name: "Hulk", + }, + }, + }, + delay: 10, + }, + ]; - it("re-suspends when calling `fetchMore` with different variables", async () => { - const { ProfiledApp } = renderPaginatedIntegrationTest(); + const cache = new InMemoryCache(); - { - const { withinDOM, snapshot } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(1); - expect(withinDOM().getByText("loading")).toBeInTheDocument(); - } + cache.writeQuery({ + query, + variables: { + id: "1", + }, + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }, + }); - { - const { withinDOM } = await ProfiledApp.takeRender(); - const items = await screen.findAllByTestId(/letter/i); - expect(items).toHaveLength(2); - expect(getItemTexts(withinDOM())).toStrictEqual(["A", "B"]); - } + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - const button = screen.getByText("Fetch more"); - const user = userEvent.setup(); - await act(() => user.click(button)); + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); - { - // parent component re-suspends - const { snapshot } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(2); - } - { - // parent component re-suspends - const { snapshot, withinDOM } = await ProfiledApp.takeRender(); - expect(snapshot.count).toBe(2); + function App() { + useTrackRenders(); + const [id, setId] = React.useState("1"); - expect(getItemTexts(withinDOM())).toStrictEqual(["C", "D"]); - } + const [queryRef, { refetch }] = useBackgroundQuery(query, { + errorPolicy: id === "1" ? "all" : "none", + variables: { id }, }); - it("properly uses `updateQuery` when calling `fetchMore`", async () => { - const { ProfiledApp } = renderPaginatedIntegrationTest({ - updateQuery: true, - }); - - { - const { withinDOM, snapshot } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(1); - expect(withinDOM().getByText("loading")).toBeInTheDocument(); - } + return ( + <> + + + + + }> + + + + + ); + } - { - const { withinDOM } = await ProfiledApp.takeRender(); + renderWithClient(, { client, wrapper: Profiler }); - const items = withinDOM().getAllByTestId(/letter/i); + { + const { snapshot } = await Profiler.takeRender(); - expect(items).toHaveLength(2); - expect(getItemTexts(withinDOM())).toStrictEqual(["A", "B"]); - } + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } - const button = screen.getByText("Fetch more"); - const user = userEvent.setup(); - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Get second character"))); + await Profiler.takeRender(); - { - const { snapshot } = await ProfiledApp.takeRender(); - // parent component re-suspends - expect(snapshot.suspenseCount).toBe(2); - } - { - const { snapshot, withinDOM } = await ProfiledApp.takeRender(); - expect(snapshot.count).toBe(2); + { + const { snapshot } = await Profiler.takeRender(); - const moreItems = withinDOM().getAllByTestId(/letter/i); - expect(moreItems).toHaveLength(4); - expect(getItemTexts(withinDOM())).toStrictEqual(["A", "B", "C", "D"]); - } + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "2", + name: "Hulk", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, }); - it("properly uses cache field policies when calling `fetchMore` without `updateQuery`", async () => { - const { ProfiledApp } = renderPaginatedIntegrationTest({ - fieldPolicies: true, - }); - - { - const { snapshot, withinDOM } = await ProfiledApp.takeRender(); - expect(snapshot.suspenseCount).toBe(1); - expect(withinDOM().getByText("loading")).toBeInTheDocument(); - } - - { - const { withinDOM } = await ProfiledApp.takeRender(); - const items = withinDOM().getAllByTestId(/letter/i); - expect(items).toHaveLength(2); - expect(getItemTexts(withinDOM())).toStrictEqual(["A", "B"]); - } + } - const button = screen.getByText("Fetch more"); - const user = userEvent.setup(); - await act(() => user.click(button)); + await act(() => user.click(screen.getByText("Get first character"))); - { - const { snapshot } = await ProfiledApp.takeRender(); - // parent component re-suspends - expect(snapshot.suspenseCount).toBe(2); - } + { + const { snapshot } = await Profiler.takeRender(); - { - const { snapshot, withinDOM } = await ProfiledApp.takeRender(); - expect(snapshot.count).toBe(2); - const moreItems = await screen.findAllByTestId(/letter/i); - expect(moreItems).toHaveLength(4); - expect(getItemTexts(withinDOM())).toStrictEqual(["A", "B", "C", "D"]); - } + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, }); - it("`fetchMore` works with startTransition to allow React to show stale UI until finished suspending", async () => { - type Variables = { - offset: number; - }; + } - interface Todo { - __typename: "Todo"; - id: string; - name: string; - completed: boolean; - } - interface Data { - todos: Todo[]; - } - const user = userEvent.setup(); + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); - const query: TypedDocumentNode = gql` - query TodosQuery($offset: Int!) { - todos(offset: $offset) { - id - name - completed - } - } - `; + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const mocks: MockedResponse[] = [ - { - request: { query, variables: { offset: 0 } }, - result: { - data: { - todos: [ - { - __typename: "Todo", - id: "1", - name: "Clean room", - completed: false, - }, - ], - }, - }, - delay: 10, - }, - { - request: { query, variables: { offset: 1 } }, - result: { - data: { - todos: [ - { - __typename: "Todo", - id: "2", - name: "Take out trash", - completed: true, - }, - ], - }, + // Ensure we render the inline error instead of the error boundary, which + // tells us the error policy was properly applied. + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", }, - delay: 10, }, - ]; - - const client = new ApolloClient({ - link: new MockLink(mocks), - cache: new InMemoryCache({ - typePolicies: { - Query: { - fields: { - todos: offsetLimitPagination(), - }, - }, - }, - }), - }); + error: new ApolloError({ graphQLErrors: [new GraphQLError("oops")] }), + networkStatus: NetworkStatus.error, + }, + }); + } +}); - function App() { - return ( - - }> - - - - ); - } +it('does not suspend when partial data is in the cache and using a "cache-first" fetch policy with returnPartialData', async () => { + const { query, mocks } = setupVariablesCase(); + const cache = new InMemoryCache(); - function SuspenseFallback() { - return

Loading

; - } + { + // Disable missing field warning + using _consoleSpy = spyOnConsole("error"); + cache.writeQuery({ + query, + // @ts-expect-error writing partial query data + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); + } - function Parent() { - const [queryRef, { fetchMore }] = useBackgroundQuery(query, { - variables: { offset: 0 }, - }); - const onFetchMoreHandler = (variables: Variables) => { - fetchMore({ variables }); - }; - return ; - } - - function Todo({ - queryRef, - onFetchMore, - }: { - onFetchMore: (variables: Variables) => void; - queryRef: QueryReference; - }) { - const { data } = useReadQuery(queryRef); - const [isPending, startTransition] = React.useTransition(); - const { todos } = data; - - return ( - <> - -
- {todos.map((todo) => ( -
- {todo.name} - {todo.completed && " (completed)"} -
- ))} -
- - ); - } + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - const ProfiledApp = profile({ Component: App, snapshotDOM: true }); - render(); + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - { - const { withinDOM } = await ProfiledApp.takeRender(); - expect(withinDOM().getByText("Loading")).toBeInTheDocument(); - } + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-first", + returnPartialData: true, + variables: { id: "1" }, + }); - { - const { withinDOM } = await ProfiledApp.takeRender(); - expect(withinDOM().getByTestId("todos")).toBeInTheDocument(); - expect(withinDOM().getByTestId("todo:1")).toBeInTheDocument(); - } + return ( + }> + + + ); + } - const button = screen.getByText("Load more"); - await act(() => user.click(button)); + renderWithClient(, { client, wrapper: Profiler }); - { - const { withinDOM } = await ProfiledApp.takeRender(); - // startTransition will avoid rendering the suspense fallback for already - // revealed content if the state update inside the transition causes the - // component to suspend. - // - // Here we should not see the suspense fallback while the component suspends - // until the todo is finished loading. Seeing the suspense fallback is an - // indication that we are suspending the component too late in the process. - expect(withinDOM().queryByText("Loading")).not.toBeInTheDocument(); - - // We can ensure this works with isPending from useTransition in the process - expect(withinDOM().getByTestId("todos")).toHaveAttribute( - "aria-busy", - "true" - ); - - // Ensure we are showing the stale UI until the new todo has loaded - expect(withinDOM().getByTestId("todo:1")).toHaveTextContent( - "Clean room" - ); - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - { - const { withinDOM } = await ProfiledApp.takeRender(); - // Eventually we should see the updated todos content once its done - // suspending. - expect(withinDOM().getByTestId("todo:2")).toHaveTextContent( - "Take out trash (completed)" - ); - expect(withinDOM().getByTestId("todo:1")).toHaveTextContent( - "Clean room" - ); - } + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, }); + } - it('honors refetchWritePolicy set to "merge"', async () => { - const user = userEvent.setup(); - - const query: TypedDocumentNode< - { primes: number[] }, - { min: number; max: number } - > = gql` - query GetPrimes($min: number, $max: number) { - primes(min: $min, max: $max) - } - `; - - interface QueryData { - primes: number[]; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const mocks = [ - { - request: { query, variables: { min: 0, max: 12 } }, - result: { data: { primes: [2, 3, 5, 7, 11] } }, - }, - { - request: { query, variables: { min: 12, max: 30 } }, - result: { data: { primes: [13, 17, 19, 23, 29] } }, - delay: 10, - }, - ]; + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const mergeParams: [number[] | undefined, number[]][] = []; - const cache = new InMemoryCache({ - typePolicies: { - Query: { - fields: { - primes: { - keyArgs: false, - merge(existing: number[] | undefined, incoming: number[]) { - mergeParams.push([existing, incoming]); - return existing ? existing.concat(incoming) : incoming; - }, - }, - }, - }, - }, - }); + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function SuspenseFallback() { - return
loading
; +it('suspends and does not use partial data from other variables in the cache when changing variables and using a "cache-first" fetch policy with returnPartialData: true', async () => { + const { query, mocks } = setupVariablesCase(); + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id } + } + `; - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, - }); + const cache = new InMemoryCache(); - function Child({ - refetch, - queryRef, - }: { - refetch: ( - variables?: Partial | undefined - ) => Promise>; - queryRef: QueryReference; - }) { - const { data, error, networkStatus } = useReadQuery(queryRef); - - return ( -
- -
{data?.primes.join(", ")}
-
{networkStatus}
-
{error?.message || "undefined"}
-
- ); - } + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); - function Parent() { - const [queryRef, { refetch }] = useBackgroundQuery(query, { - variables: { min: 0, max: 12 }, - refetchWritePolicy: "merge", - }); - return ; - } + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function App() { - return ( - - }> - - - - ); - } + function App({ id }: { id: string }) { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-first", + returnPartialData: true, + variables: { id }, + }); - render(); + return ( + }> + + + ); + } - await waitFor(() => { - expect(screen.getByTestId("primes")).toHaveTextContent( - "2, 3, 5, 7, 11" - ); - }); - expect(screen.getByTestId("network-status")).toHaveTextContent( - "7" // ready - ); - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); - expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + const { rerender } = renderWithMocks(, { + cache, + mocks, + wrapper: Profiler, + }); - await act(() => user.click(screen.getByText("Refetch"))); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - await waitFor(() => { - expect(screen.getByTestId("primes")).toHaveTextContent( - "2, 3, 5, 7, 11, 13, 17, 19, 23, 29" - ); - }); - expect(screen.getByTestId("network-status")).toHaveTextContent( - "7" // ready - ); - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); - expect(mergeParams).toEqual([ - [undefined, [2, 3, 5, 7, 11]], - [ - [2, 3, 5, 7, 11], - [13, 17, 19, 23, 29], - ], - ]); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, }); + } - it('defaults refetchWritePolicy to "overwrite"', async () => { - const user = userEvent.setup(); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const query: TypedDocumentNode< - { primes: number[] }, - { min: number; max: number } - > = gql` - query GetPrimes($min: number, $max: number) { - primes(min: $min, max: $max) - } - `; + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - interface QueryData { - primes: number[]; - } + rerender(); - const mocks = [ - { - request: { query, variables: { min: 0, max: 12 } }, - result: { data: { primes: [2, 3, 5, 7, 11] } }, - }, - { - request: { query, variables: { min: 12, max: 30 } }, - result: { data: { primes: [13, 17, 19, 23, 29] } }, - delay: 10, - }, - ]; + { + const { renderedComponents } = await Profiler.takeRender(); - const mergeParams: [number[] | undefined, number[]][] = []; - const cache = new InMemoryCache({ - typePolicies: { - Query: { - fields: { - primes: { - keyArgs: false, - merge(existing: number[] | undefined, incoming: number[]) { - mergeParams.push([existing, incoming]); - return existing ? existing.concat(incoming) : incoming; - }, - }, - }, - }, - }, - }); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - function SuspenseFallback() { - return
loading
; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, - }); + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "2", name: "Black Widow" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - function Child({ - refetch, - queryRef, - }: { - refetch: ( - variables?: Partial | undefined - ) => Promise>; - queryRef: QueryReference; - }) { - const { data, error, networkStatus } = useReadQuery(queryRef); - - return ( -
- -
{data?.primes.join(", ")}
-
{networkStatus}
-
{error?.message || "undefined"}
-
- ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function Parent() { - const [queryRef, { refetch }] = useBackgroundQuery(query, { - variables: { min: 0, max: 12 }, - }); - return ; - } +it('suspends when partial data is in the cache and using a "network-only" fetch policy with returnPartialData', async () => { + const { query, mocks } = setupVariablesCase(); - function App() { - return ( - - }> - - - - ); + const partialQuery = gql` + query ($id: String!) { + character(id: $id) { + id } + } + `; - render(); + const cache = new InMemoryCache(); - await waitFor(() => { - expect(screen.getByTestId("primes")).toHaveTextContent( - "2, 3, 5, 7, 11" - ); - }); - expect(screen.getByTestId("network-status")).toHaveTextContent( - "7" // ready - ); - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); - expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + cache.writeQuery({ + query: partialQuery, + variables: { id: "1" }, + data: { character: { __typename: "Character", id: "1" } }, + }); - await act(() => user.click(screen.getByText("Refetch"))); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - await waitFor(() => { - expect(screen.getByTestId("primes")).toHaveTextContent( - "13, 17, 19, 23, 29" - ); - }); - expect(screen.getByTestId("network-status")).toHaveTextContent( - "7" // ready - ); - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); - expect(mergeParams).toEqual([ - [undefined, [2, 3, 5, 7, 11]], - [undefined, [13, 17, 19, 23, 29]], - ]); - }); + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - it('does not suspend when partial data is in the cache and using a "cache-first" fetch policy with returnPartialData', async () => { - interface Data { - character: { - id: string; - name: string; - }; - } + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "network-only", + returnPartialData: true, + variables: { id: "1" }, + }); - const fullQuery: TypedDocumentNode = gql` - query { - character { - id - name - } - } - `; + return ( + }> + + + ); + } - const partialQuery = gql` - query { - character { - id - } - } - `; - const mocks = [ - { - request: { query: fullQuery }, - result: { data: { character: { id: "1", name: "Doctor Strange" } } }, - }, - ]; + renderWithClient(, { client, wrapper: Profiler }); - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - }; + { + const { renderedComponents } = await Profiler.takeRender(); - const cache = new InMemoryCache(); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, - }); + { + const { snapshot } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, - }); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - function App() { - return ( - - }> - - - - ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - function SuspenseFallback() { - renders.suspenseCount++; - return

Loading

; - } +it('suspends when partial data is in the cache and using a "no-cache" fetch policy with returnPartialData', async () => { + using _consoleSpy = spyOnConsole("warn"); + const { query, mocks } = setupVariablesCase(); - function Parent() { - const [queryRef] = useBackgroundQuery(fullQuery, { - fetchPolicy: "cache-first", - returnPartialData: true, - }); - return ; + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id } + } + `; - function Todo({ - queryRef, - }: { - queryRef: QueryReference>; - }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - renders.count++; - - return ( - <> -
{data.character?.id}
-
{data.character?.name}
-
{networkStatus}
-
{error?.message || "undefined"}
- - ); - } + const cache = new InMemoryCache(); - render(); + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); - expect(renders.suspenseCount).toBe(0); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - expect(screen.getByTestId("character-name")).toHaveTextContent(""); - expect(screen.getByTestId("network-status")).toHaveTextContent("1"); // loading - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - await waitFor(() => { - expect(screen.getByTestId("character-name")).toHaveTextContent( - "Doctor Strange" - ); - }); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - expect(renders.count).toBe(2); - expect(renders.suspenseCount).toBe(0); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + variables: { id: "1" }, }); - it('suspends and does not use partial data when changing variables and using a "cache-first" fetch policy with returnPartialData', async () => { - const partialQuery = gql` - query ($id: ID!) { - character(id: $id) { - id - } - } - `; - - const cache = new InMemoryCache(); - - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, - variables: { id: "1" }, - }); - - const { renders, mocks, rerender } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - cache, - options: { - fetchPolicy: "cache-first", - returnPartialData: true, - }, - }); - expect(renders.suspenseCount).toBe(0); + return ( + }> + + + ); + } - expect(await screen.findByText("1 - Spider-Man")).toBeInTheDocument(); + renderWithClient(, { client, wrapper: Profiler }); - rerender({ variables: { id: "2" } }); + { + const { renderedComponents } = await Profiler.takeRender(); - expect(await screen.findByText("2 - Black Widow")).toBeInTheDocument(); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(renders.frames[2]).toMatchObject({ - ...mocks[1].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }); + { + const { snapshot } = await Profiler.takeRender(); - expect(renders.count).toBe(3); - expect(renders.suspenseCount).toBe(1); - expect(renders.frames).toMatchObject([ - { - data: { character: { id: "1" } }, - networkStatus: NetworkStatus.loading, - error: undefined, - }, - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - { - ...mocks[1].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - ]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - it('suspends when partial data is in the cache and using a "network-only" fetch policy with returnPartialData', async () => { - interface Data { - character: { - id: string; - name: string; - }; - } + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); - const fullQuery: TypedDocumentNode = gql` - query { - character { - id - name - } - } - `; +it('warns when using returnPartialData with a "no-cache" fetch policy', async () => { + using _consoleSpy = spyOnConsole("warn"); - const partialQuery = gql` - query { - character { - id - } - } - `; - const mocks = [ - { - request: { query: fullQuery }, - result: { data: { character: { id: "1", name: "Doctor Strange" } } }, - }, - ]; - - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: { - data: DeepPartial; - networkStatus: NetworkStatus; - error: ApolloError | undefined; - }[]; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; + const query: TypedDocumentNode = gql` + query UserQuery { + greeting + } + `; + const mocks = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + }, + ]; - const cache = new InMemoryCache(); + renderHook( + () => + useBackgroundQuery(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, - }); + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn).toHaveBeenCalledWith( + "Using `returnPartialData` with a `no-cache` fetch policy has no effect. To read partial data from the cache, consider using an alternate fetch policy." + ); +}); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, - }); +it('does not suspend when partial data is in the cache and using a "cache-and-network" fetch policy with returnPartialData', async () => { + const { query, mocks } = setupVariablesCase(); - function App() { - return ( - - }> - - - - ); + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id } + } + `; - function SuspenseFallback() { - renders.suspenseCount++; - return

Loading

; - } + const cache = new InMemoryCache(); - function Parent() { - const [queryRef] = useBackgroundQuery(fullQuery, { - fetchPolicy: "network-only", - returnPartialData: true, - }); + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); - return ; - } + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); - function Todo({ - queryRef, - }: { - queryRef: QueryReference>; - }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - renders.frames.push({ data, networkStatus, error }); - renders.count++; - return ( - <> -
{data.character?.id}
-
{data.character?.name}
-
{networkStatus}
-
{error?.message || "undefined"}
- - ); - } + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - render(); + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-and-network", + returnPartialData: true, + variables: { id: "1" }, + }); - expect(renders.suspenseCount).toBe(1); + return ( + }> + + + ); + } - await waitFor(() => { - expect(screen.getByTestId("character-name")).toHaveTextContent( - "Doctor Strange" - ); - }); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + renderWithClient(, { client, wrapper: Profiler }); - expect(renders.count).toBe(1); - expect(renders.suspenseCount).toBe(1); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(renders.frames).toMatchObject([ - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, - }, - ]); + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, }); + } - it('suspends when partial data is in the cache and using a "no-cache" fetch policy with returnPartialData', async () => { - using _consoleSpy = spyOnConsole("warn"); - interface Data { - character: { - id: string; - name: string; - }; - } + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - const fullQuery: TypedDocumentNode = gql` - query { - character { - id - name - } - } + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it('suspends and does not use partial data when changing variables and using a "cache-and-network" fetch policy with returnPartialData', async () => { + const { query, mocks } = setupVariablesCase(); + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id + } + } + `; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App({ id }: { id: string }) { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-and-network", + returnPartialData: true, + variables: { id }, + }); + + return ( + }> + + + ); + } + + const { rerender } = renderWithMocks(, { + cache, + mocks, + wrapper: Profiler, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + rerender(); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "2", name: "Black Widow" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it('does not suspend deferred queries with partial data in the cache and using a "cache-first" fetch policy with `returnPartialData`', async () => { + interface QueryData { + greeting: { + __typename: string; + message?: string; + recipient?: { + __typename: string; + name: string; + }; + }; + } + + const query: TypedDocumentNode = gql` + query { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + const cache = new InMemoryCache(); + + // We are intentionally writing partial data to the cache. Supress console + // warnings to avoid unnecessary noise in the test. + { + using _consoleSpy = spyOnConsole("error"); + cache.writeQuery({ + query, + data: { + greeting: { + __typename: "Greeting", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + }); + } + + const client = new ApolloClient({ link, cache }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { + fetchPolicy: "cache-first", + returnPartialData: true, + }); + + return ( + }> + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + link.simulateResult({ + result: { + data: { + greeting: { message: "Hello world", __typename: "Greeting" }, + }, + hasNext: true, + }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + link.simulateResult( + { + result: { + incremental: [ + { + data: { + __typename: "Greeting", + recipient: { name: "Alice", __typename: "Person" }, + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it.each([ + "cache-first", + "network-only", + "cache-and-network", +])( + 'responds to cache updates in strict mode while using a "%s" fetch policy', + async (fetchPolicy) => { + const { query, mocks } = setupSimpleCase(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query, { fetchPolicy }); + + return ( + }> + + + ); + } + + renderWithClient(, { + client, + wrapper: ({ children }) => ( + + {children} + + ), + }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "Updated hello" }, + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Updated hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + } +); + +describe("refetch", () => { + it("re-suspends when calling `refetch`", async () => { + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + const mocks: MockedResponse[] = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched)", + }, + }, + }, + delay: 10, + }, + ]; + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + <> + + }> + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + // parent component re-suspends + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it("re-suspends when calling `refetch` with new variables", async () => { + const { query, mocks } = setupVariablesCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + <> + + }> + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "2", + name: "Black Widow", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it("re-suspends multiple times when calling `refetch` multiple times", async () => { + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + const mocks: MockedResponse[] = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched)", + }, + }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched again)", + }, + }, + }, + delay: 10, + }, + ]; + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + <> + + }> + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + const button = screen.getByText("Refetch"); + + await act(() => user.click(button)); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(button)); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (refetched again)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it("throws errors when errors are returned after calling `refetch`", async () => { + using _consoleSpy = spyOnConsole("error"); + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const mocks: MockedResponse[] = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + <> + + }> + + + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ErrorFallback"]); + expect(snapshot.error).toEqual( + new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }) + ); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it('ignores errors returned after calling `refetch` when errorPolicy is set to "ignore"', async () => { + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const mocks = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + errorPolicy: "ignore", + }); + + return ( + <> + + }> + + + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it('returns errors after calling `refetch` when errorPolicy is set to "all"', async () => { + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const mocks = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + errorPolicy: "all", + }); + + return ( + <> + + }> + + + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }), + networkStatus: NetworkStatus.error, + }, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it('handles partial data results after calling `refetch` when errorPolicy is set to "all"', async () => { + const { query, mocks: defaultMocks } = setupVariablesCase(); + const user = userEvent.setup(); + const mocks = [ + ...defaultMocks, + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { __typename: "Character", id: "1", name: null } }, + errors: [new GraphQLError("Something went wrong")], + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + errorPolicy: "all", + }); + + return ( + <> + + }> + + + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + error: null, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: null, + }, + }, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }), + networkStatus: NetworkStatus.error, + }, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); + + it("can refetch after error is encountered", async () => { + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + + const user = userEvent.setup(); + + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: null, + errors: [new GraphQLError("Oops couldn't fetch")], + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback } = createDefaultTrackedComponents(Profiler); + + function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { + useTrackRenders(); + Profiler.mergeSnapshot({ error }); + + return ; + } + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + }> + refetch()} + FallbackComponent={ErrorFallback} + > + + + + ); + } + + function Todo({ queryRef }: { queryRef: QueryRef }) { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + // Disable error message shown in the console due to an uncaught error. + using _consoleSpy = spyOnConsole("error"); + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ErrorFallback]); + expect(snapshot).toEqual({ + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Oops couldn't fetch")], + }), + result: null, + }); + } + + await act(() => user.click(screen.getByText("Retry"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([Todo]); + expect(snapshot).toEqual({ + // TODO: We should reset the snapshot between renders to better capture + // the actual result. This makes it seem like the error is rendered, but + // in this is just leftover from the previous snapshot. + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Oops couldn't fetch")], + }), + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + }); + + it("throws errors on refetch after error is encountered after first fetch with error", async () => { + // Disable error message shown in the console due to an uncaught error. + using _consoleSpy = spyOnConsole("error"); + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + + const user = userEvent.setup(); + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: null, + errors: [new GraphQLError("Oops couldn't fetch")], + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: null, + errors: [new GraphQLError("Oops couldn't fetch again")], + }, + delay: 10, + }, + ]; + + const Profiler = createErrorProfiler(); + const { SuspenseFallback } = createDefaultTrackedComponents(Profiler); + + function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { + useTrackRenders(); + Profiler.mergeSnapshot({ error }); + + return ; + } + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + return ( + }> + refetch()} + FallbackComponent={ErrorFallback} + > + + + + ); + } + + function Todo({ queryRef }: { queryRef: QueryRef }) { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ErrorFallback]); + expect(snapshot).toEqual({ + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Oops couldn't fetch")], + }), + result: null, + }); + } + + await act(() => user.click(screen.getByText("Retry"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ErrorFallback]); + expect(snapshot).toEqual({ + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Oops couldn't fetch again")], + }), + result: null, + }); + } + }); + + it("`refetch` works with startTransition to allow React to show stale UI until finished suspending", async () => { + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + + const user = userEvent.setup(); + + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + }, + delay: 10, + }, + ]; + + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { id: "1" }, + }); + + Profiler.mergeSnapshot({ isPending }); + + return ( + <> + + }> + + + + ); + } + + renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component + // suspends until the todo is finished loading. Seeing the suspense + // fallback is an indication that we are suspending the component too late + // in the process. + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + // Eventually we should see the updated todo content once its done + // suspending. + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + }); + + it('honors refetchWritePolicy set to "merge"', async () => { + const user = userEvent.setup(); + + const query: TypedDocumentNode = + gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { min: 0, max: 12 }, + refetchWritePolicy: "merge", + }); + + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + ]); + } + }); + + it('defaults refetchWritePolicy to "overwrite"', async () => { + const user = userEvent.setup(); + + const query: TypedDocumentNode = + gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } `; - const partialQuery = gql` - query { - character { - id - } - } - `; - const mocks = [ - { - request: { query: fullQuery }, - result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { refetch }] = useBackgroundQuery(query, { + variables: { min: 0, max: 12 }, + }); + + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [undefined, [13, 17, 19, 23, 29]], + ]); + } + }); +}); + +describe("fetchMore", () => { + it("re-suspends when calling `fetchMore` with different variables", async () => { + const { query, link } = setupPaginatedCase(); + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, }, - ]; - - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: { - data: DeepPartial; - networkStatus: NetworkStatus; - error: ApolloError | undefined; - }[]; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; + }, + }); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query); + + return ( + <> + + }> + + + + ); + } + + renderWithMocks(, { cache, link, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); - const cache = new InMemoryCache(); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, + { + const { snapshot } = await Profiler.takeRender(); + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 1, letter: "A" }, + { __typename: "Letter", position: 2, letter: "B" }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, + await act(() => user.click(screen.getByText("Fetch more"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 3, letter: "C" }, + { __typename: "Letter", position: 4, letter: "D" }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function App() { - return ( - - }> - - - - ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); - function SuspenseFallback() { - renders.suspenseCount++; - return

Loading

; - } + it("properly uses `updateQuery` when calling `fetchMore`", async () => { + const { query, link } = setupPaginatedCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - function Parent() { - const [queryRef] = useBackgroundQuery(fullQuery, { - fetchPolicy: "no-cache", - returnPartialData: true, - }); + function App() { + useTrackRenders(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query); - return ; - } + return ( + <> + + }> + + + + ); + } - function Todo({ - queryRef, - }: { - queryRef: QueryReference>; - }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - renders.frames.push({ data, networkStatus, error }); - renders.count++; - return ( - <> -
{data.character?.id}
-
{data.character?.name}
-
{networkStatus}
-
{error?.message || "undefined"}
- - ); - } + renderWithMocks(, { link, wrapper: Profiler }); - render(); + { + const { renderedComponents } = await Profiler.takeRender(); - expect(renders.suspenseCount).toBe(1); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - await waitFor(() => { - expect(screen.getByTestId("character-name")).toHaveTextContent( - "Doctor Strange" - ); + { + const { snapshot } = await Profiler.takeRender(); + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 1, letter: "A" }, + { __typename: "Letter", position: 2, letter: "B" }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + } - expect(renders.count).toBe(1); - expect(renders.suspenseCount).toBe(1); + await act(() => user.click(screen.getByText("Fetch more"))); - expect(renders.frames).toMatchObject([ - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 1, letter: "A" }, + { __typename: "Letter", position: 2, letter: "B" }, + { __typename: "Letter", position: 3, letter: "C" }, + { __typename: "Letter", position: 4, letter: "D" }, + ], }, - ]); - }); + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - it('warns when using returnPartialData with a "no-cache" fetch policy', async () => { - using _consoleSpy = spyOnConsole("warn"); + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); - const query: TypedDocumentNode = gql` - query UserQuery { - greeting - } - `; - const mocks = [ - { - request: { query }, - result: { data: { greeting: "Hello" } }, + it("properly uses cache field policies when calling `fetchMore` without `updateQuery`", async () => { + const { query, link } = setupPaginatedCase(); + const user = userEvent.setup(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: concatPagination(), + }, + }, }, - ]; + }), + }); - renderSuspenseHook( - () => - useBackgroundQuery(query, { - fetchPolicy: "no-cache", - returnPartialData: true, - }), - { mocks } - ); + function App() { + useTrackRenders(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query); - expect(console.warn).toHaveBeenCalledTimes(1); - expect(console.warn).toHaveBeenCalledWith( - "Using `returnPartialData` with a `no-cache` fetch policy has no effect. To read partial data from the cache, consider using an alternate fetch policy." + return ( + <> + + }> + + + ); - }); + } - it('does not suspend when partial data is in the cache and using a "cache-and-network" fetch policy with returnPartialData', async () => { - interface Data { - character: { - id: string; - name: string; - }; - } + renderWithClient(, { client, wrapper: Profiler }); - const fullQuery: TypedDocumentNode = gql` - query { - character { - id - name - } - } - `; + { + const { renderedComponents } = await Profiler.takeRender(); - const partialQuery = gql` - query { - character { - id - } - } - `; - const mocks = [ - { - request: { query: fullQuery }, - result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 1, letter: "A" }, + { __typename: "Letter", position: 2, letter: "B" }, + ], }, - ]; - - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: { - data: DeepPartial; - networkStatus: NetworkStatus; - error: ApolloError | undefined; - }[]; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } - const cache = new InMemoryCache(); + await act(() => user.click(screen.getByText("Fetch more"))); - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, - }); + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); - const client = new ApolloClient({ - link: new MockLink(mocks), - cache, + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", position: 1, letter: "A" }, + { __typename: "Letter", position: 2, letter: "B" }, + { __typename: "Letter", position: 3, letter: "C" }, + { __typename: "Letter", position: 4, letter: "D" }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }); + } - function App() { - return ( - - }> - - - - ); - } + await expect(Profiler).not.toRerender({ timeout: 50 }); + }); - function SuspenseFallback() { - renders.suspenseCount++; - return

Loading

; - } + it("`fetchMore` works with startTransition to allow React to show stale UI until finished suspending", async () => { + type Variables = { + offset: number; + }; + + interface Todo { + __typename: "Todo"; + id: string; + name: string; + completed: boolean; + } - function Parent() { - const [queryRef] = useBackgroundQuery(fullQuery, { - fetchPolicy: "cache-and-network", - returnPartialData: true, - }); + interface Data { + todos: Todo[]; + } - return ; - } + const user = userEvent.setup(); - function Todo({ - queryRef, - }: { - queryRef: QueryReference>; - }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - renders.frames.push({ data, networkStatus, error }); - renders.count++; - return ( - <> -
{data.character?.id}
-
{data.character?.name}
-
{networkStatus}
-
{error?.message || "undefined"}
- - ); + const query: TypedDocumentNode = gql` + query TodosQuery($offset: Int!) { + todos(offset: $offset) { + id + name + completed + } } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { offset: 0 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, + }, + delay: 10, + }, + { + request: { query, variables: { offset: 1 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, + }, + delay: 10, + }, + ]; - render(); + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - expect(renders.suspenseCount).toBe(0); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - // name is not present yet, since it's missing in partial data - expect(screen.getByTestId("character-name")).toHaveTextContent(""); - expect(screen.getByTestId("network-status")).toHaveTextContent("1"); // loading - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + todos: offsetLimitPagination(), + }, + }, + }, + }), + }); - await waitFor(() => { - expect(screen.getByTestId("character-name")).toHaveTextContent( - "Doctor Strange" - ); + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query, { + variables: { offset: 0 }, }); - expect(screen.getByTestId("character-id")).toHaveTextContent("1"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); - expect(renders.count).toBe(2); - expect(renders.suspenseCount).toBe(0); + Profiler.mergeSnapshot({ isPending }); - expect(renders.frames).toMatchObject([ - { - data: { character: { id: "1" } }, - networkStatus: NetworkStatus.loading, + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, error: undefined, - }, - { - ...mocks[0].result, networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Load more"))); + + { + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component suspends + // until the todo is finished loading. Seeing the suspense fallback is an + // indication that we are suspending the component too late in the process. + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, error: undefined, + networkStatus: NetworkStatus.ready, }, - ]); - }); - - it('suspends and does not use partial data when changing variables and using a "cache-and-network" fetch policy with returnPartialData', async () => { - const partialQuery = gql` - query ($id: ID!) { - character(id: $id) { - id - } - } - `; - - const cache = new InMemoryCache(); - - cache.writeQuery({ - query: partialQuery, - data: { character: { id: "1" } }, - variables: { id: "1" }, }); + } - const { renders, mocks, rerender } = renderVariablesIntegrationTest({ - variables: { id: "1" }, - cache, - options: { - fetchPolicy: "cache-and-network", - returnPartialData: true, + { + // Eventually we should see the updated todos content once its done + // suspending. + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }, }); + } - expect(renders.suspenseCount).toBe(0); + await expect(Profiler).not.toRerender(); + }); - expect(await screen.findByText("1 - Spider-Man")).toBeInTheDocument(); + // https://github.com/apollographql/apollo-client/issues/11708 + it("`fetchMore` works with startTransition when setting errorPolicy as default option in ApolloClient constructor", async () => { + type Variables = { + offset: number; + }; - rerender({ variables: { id: "2" } }); + interface Todo { + __typename: "Todo"; + id: string; + name: string; + completed: boolean; + } + interface Data { + todos: Todo[]; + } + const user = userEvent.setup(); - expect(await screen.findByText("2 - Black Widow")).toBeInTheDocument(); + const query: TypedDocumentNode = gql` + query TodosQuery($offset: Int!) { + todos(offset: $offset) { + id + name + completed + } + } + `; - expect(renders.count).toBe(3); - expect(renders.suspenseCount).toBe(1); - expect(renders.frames).toMatchObject([ - { - data: { character: { id: "1" } }, - networkStatus: NetworkStatus.loading, - error: undefined, - }, - { - ...mocks[0].result, - networkStatus: NetworkStatus.ready, - error: undefined, + const mocks: MockedResponse[] = [ + { + request: { query, variables: { offset: 0 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, }, - { - ...mocks[1].result, - networkStatus: NetworkStatus.ready, - error: undefined, + delay: 10, + }, + { + request: { query, variables: { offset: 1 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, }, - ]); - }); - - it('does not suspend deferred queries with partial data in the cache and using a "cache-first" fetch policy with `returnPartialData`', async () => { - interface QueryData { - greeting: { - __typename: string; - message?: string; - recipient?: { - __typename: string; - name: string; - }; - }; - } + delay: 10, + }, + ]; - const query: TypedDocumentNode = gql` - query { - greeting { - message - ... on Greeting @defer { - recipient { - name - } - } - } - } - `; + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); - const link = new MockSubscriptionLink(); - const cache = new InMemoryCache(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); - // We are intentionally writing partial data to the cache. Supress console - // warnings to avoid unnecessary noise in the test. - { - using _consoleSpy = spyOnConsole("error"); - cache.writeQuery({ - query, - data: { - greeting: { - __typename: "Greeting", - recipient: { __typename: "Person", name: "Cached Alice" }, + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + todos: offsetLimitPagination(), }, }, - }); - } - - interface Renders { - errors: Error[]; - errorCount: number; - suspenseCount: number; - count: number; - frames: { - data: DeepPartial; - networkStatus: NetworkStatus; - error: ApolloError | undefined; - }[]; - } - const renders: Renders = { - errors: [], - errorCount: 0, - suspenseCount: 0, - count: 0, - frames: [], - }; + }, + }), + defaultOptions: { + watchQuery: { + errorPolicy: "all", + }, + }, + }); - const client = new ApolloClient({ - link, - cache, + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query, { + variables: { offset: 0 }, }); - function App() { - return ( - - }> - - - - ); - } - - function SuspenseFallback() { - renders.suspenseCount++; - return

Loading

; - } + Profiler.mergeSnapshot({ isPending }); - function Parent() { - const [queryRef] = useBackgroundQuery(query, { - fetchPolicy: "cache-first", - returnPartialData: true, - }); + return ( + <> + + }> + + + + ); + } - return ; - } + renderWithClient(, { client, wrapper: Profiler }); - function Todo({ - queryRef, - }: { - queryRef: QueryReference>; - }) { - const { data, networkStatus, error } = useReadQuery(queryRef); - renders.frames.push({ data, networkStatus, error }); - renders.count++; - return ( - <> -
{data.greeting?.message}
-
{data.greeting?.recipient?.name}
-
{networkStatus}
-
{error?.message || "undefined"}
- - ); - } + { + const { renderedComponents } = await Profiler.takeRender(); - render(); + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } - expect(renders.suspenseCount).toBe(0); - expect(screen.getByTestId("recipient")).toHaveTextContent("Cached Alice"); - // message is not present yet, since it's missing in partial data - expect(screen.getByTestId("message")).toHaveTextContent(""); - expect(screen.getByTestId("network-status")).toHaveTextContent("1"); // loading - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + { + const { snapshot } = await Profiler.takeRender(); - link.simulateResult({ + expect(snapshot).toEqual({ + isPending: false, result: { data: { - greeting: { message: "Hello world", __typename: "Greeting" }, + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], }, - hasNext: true, + error: undefined, + networkStatus: NetworkStatus.ready, }, }); + } - await waitFor(() => { - expect(screen.getByTestId("message")).toHaveTextContent("Hello world"); - }); - expect(screen.getByTestId("recipient")).toHaveTextContent("Cached Alice"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + await act(() => user.click(screen.getByText("Load more"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - link.simulateResult({ + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: true, result: { - incremental: [ - { - data: { - __typename: "Greeting", - recipient: { name: "Alice", __typename: "Person" }, + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, }, - path: ["greeting"], - }, - ], - hasNext: false, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, }, }); + } - await waitFor(() => { - expect(screen.getByTestId("recipient").textContent).toEqual("Alice"); - }); - expect(screen.getByTestId("message")).toHaveTextContent("Hello world"); - expect(screen.getByTestId("network-status")).toHaveTextContent("7"); // ready - expect(screen.getByTestId("error")).toHaveTextContent("undefined"); + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); - expect(renders.count).toBe(3); - expect(renders.suspenseCount).toBe(0); - expect(renders.frames).toMatchObject([ - { - data: { - greeting: { - __typename: "Greeting", - recipient: { __typename: "Person", name: "Cached Alice" }, - }, - }, - networkStatus: NetworkStatus.loading, - error: undefined, - }, - { + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { data: { - greeting: { - __typename: "Greeting", - message: "Hello world", - recipient: { __typename: "Person", name: "Cached Alice" }, - }, + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], }, - networkStatus: NetworkStatus.ready, error: undefined, - }, - { - data: { - greeting: { - __typename: "Greeting", - message: "Hello world", - recipient: { __typename: "Person", name: "Alice" }, - }, - }, networkStatus: NetworkStatus.ready, - error: undefined, }, - ]); - }); + }); + } + + await expect(Profiler).not.toRerender(); }); +}); - describe.skip("type tests", () => { - it("returns unknown when TData cannot be inferred", () => { - const query = gql` - query { - hello - } - `; +describe.skip("type tests", () => { + it("returns unknown when TData cannot be inferred", () => { + const query = gql` + query { + hello + } + `; + + const [queryRef] = useBackgroundQuery(query); + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + }); - const [queryRef] = useBackgroundQuery(query); - const { data } = useReadQuery(queryRef); + it("disallows wider variables type than specified", () => { + const { query } = setupVariablesCase(); - expectTypeOf(data).toEqualTypeOf(); + useBackgroundQuery(query, { + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, }); + }); - it("disallows wider variables type than specified", () => { - const { query } = useVariablesIntegrationTestCase(); + it("returns TData in default case", () => { + const { query } = setupVariablesCase(); - // @ts-expect-error should not allow wider TVariables type - useBackgroundQuery(query, { variables: { id: "1", foo: "bar" } }); - }); + const [inferredQueryRef] = useBackgroundQuery(query); + const { data: inferred } = useReadQuery(inferredQueryRef); - it("returns TData in default case", () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf(); - const [inferredQueryRef] = useBackgroundQuery(query); - const { data: inferred } = useReadQuery(inferredQueryRef); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query); - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf(); + const { data: explicit } = useReadQuery(explicitQueryRef); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - const { data: explicit } = useReadQuery(explicitQueryRef); + it('returns TData | undefined with errorPolicy: "ignore"', () => { + const { query } = setupVariablesCase(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [inferredQueryRef] = useBackgroundQuery(query, { + errorPolicy: "ignore", }); + const { data: inferred } = useReadQuery(inferredQueryRef); - it('returns TData | undefined with errorPolicy: "ignore"', () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf(); - const [inferredQueryRef] = useBackgroundQuery(query, { - errorPolicy: "ignore", - }); - const { data: inferred } = useReadQuery(inferredQueryRef); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + errorPolicy: "ignore", + }); - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf(); + const { data: explicit } = useReadQuery(explicitQueryRef); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - errorPolicy: "ignore", - }); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - const { data: explicit } = useReadQuery(explicitQueryRef); + it('returns TData | undefined with errorPolicy: "all"', () => { + const { query } = setupVariablesCase(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [inferredQueryRef] = useBackgroundQuery(query, { + errorPolicy: "all", }); + const { data: inferred } = useReadQuery(inferredQueryRef); - it('returns TData | undefined with errorPolicy: "all"', () => { - const { query } = useVariablesIntegrationTestCase(); - - const [inferredQueryRef] = useBackgroundQuery(query, { - errorPolicy: "all", - }); - const { data: inferred } = useReadQuery(inferredQueryRef); - - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf(); - - const [explicitQueryRef] = useBackgroundQuery(query, { - errorPolicy: "all", - }); - const { data: explicit } = useReadQuery(explicitQueryRef); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [explicitQueryRef] = useBackgroundQuery(query, { + errorPolicy: "all", }); + const { data: explicit } = useReadQuery(explicitQueryRef); - it('returns TData with errorPolicy: "none"', () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - const [inferredQueryRef] = useBackgroundQuery(query, { - errorPolicy: "none", - }); - const { data: inferred } = useReadQuery(inferredQueryRef); + it('returns TData with errorPolicy: "none"', () => { + const { query } = setupVariablesCase(); - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf(); + const [inferredQueryRef] = useBackgroundQuery(query, { + errorPolicy: "none", + }); + const { data: inferred } = useReadQuery(inferredQueryRef); - const [explicitQueryRef] = useBackgroundQuery(query, { - errorPolicy: "none", - }); - const { data: explicit } = useReadQuery(explicitQueryRef); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [explicitQueryRef] = useBackgroundQuery(query, { + errorPolicy: "none", }); + const { data: explicit } = useReadQuery(explicitQueryRef); - it("returns DeepPartial with returnPartialData: true", () => { - const { query } = useVariablesIntegrationTestCase(); - - const [inferredQueryRef] = useBackgroundQuery(query, { - returnPartialData: true, - }); - const { data: inferred } = useReadQuery(inferredQueryRef); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - expectTypeOf(inferred).toEqualTypeOf>(); - expectTypeOf(inferred).not.toEqualTypeOf(); + it("returns DeepPartial with returnPartialData: true", () => { + const { query } = setupVariablesCase(); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - returnPartialData: true, - }); + const [inferredQueryRef] = useBackgroundQuery(query, { + returnPartialData: true, + }); + const { data: inferred } = useReadQuery(inferredQueryRef); - const { data: explicit } = useReadQuery(explicitQueryRef); + expectTypeOf(inferred).toEqualTypeOf>(); + expectTypeOf(inferred).not.toEqualTypeOf(); - expectTypeOf(explicit).toEqualTypeOf>(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + returnPartialData: true, }); - it("returns TData with returnPartialData: false", () => { - const { query } = useVariablesIntegrationTestCase(); + const { data: explicit } = useReadQuery(explicitQueryRef); - const [inferredQueryRef] = useBackgroundQuery(query, { - returnPartialData: false, - }); - const { data: inferred } = useReadQuery(inferredQueryRef); - - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf< - DeepPartial - >(); - - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - returnPartialData: false, - }); + expectTypeOf(explicit).toEqualTypeOf>(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - const { data: explicit } = useReadQuery(explicitQueryRef); + it("returns TData with returnPartialData: false", () => { + const { query } = setupVariablesCase(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf< - DeepPartial - >(); + const [inferredQueryRef] = useBackgroundQuery(query, { + returnPartialData: false, }); + const { data: inferred } = useReadQuery(inferredQueryRef); - it("returns TData when passing an option that does not affect TData", () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf>(); - const [inferredQueryRef] = useBackgroundQuery(query, { - fetchPolicy: "no-cache", - }); - const { data: inferred } = useReadQuery(inferredQueryRef); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + returnPartialData: false, + }); - expectTypeOf(inferred).toEqualTypeOf(); - expectTypeOf(inferred).not.toEqualTypeOf< - DeepPartial - >(); + const { data: explicit } = useReadQuery(explicitQueryRef); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - fetchPolicy: "no-cache", - }); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf>(); + }); - const { data: explicit } = useReadQuery(explicitQueryRef); + it("returns TData when passing an option that does not affect TData", () => { + const { query } = setupVariablesCase(); - expectTypeOf(explicit).toEqualTypeOf(); - expectTypeOf(explicit).not.toEqualTypeOf< - DeepPartial - >(); + const [inferredQueryRef] = useBackgroundQuery(query, { + fetchPolicy: "no-cache", }); + const { data: inferred } = useReadQuery(inferredQueryRef); - it("handles combinations of options", () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(inferred).toEqualTypeOf(); + expectTypeOf(inferred).not.toEqualTypeOf>(); - const [inferredPartialDataIgnoreQueryRef] = useBackgroundQuery(query, { - returnPartialData: true, - errorPolicy: "ignore", - }); - const { data: inferredPartialDataIgnore } = useReadQuery( - inferredPartialDataIgnoreQueryRef - ); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + fetchPolicy: "no-cache", + }); - expectTypeOf(inferredPartialDataIgnore).toEqualTypeOf< - DeepPartial | undefined - >(); - expectTypeOf( - inferredPartialDataIgnore - ).not.toEqualTypeOf(); - - const [explicitPartialDataIgnoreQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - returnPartialData: true, - errorPolicy: "ignore", - }); + const { data: explicit } = useReadQuery(explicitQueryRef); - const { data: explicitPartialDataIgnore } = useReadQuery( - explicitPartialDataIgnoreQueryRef - ); + expectTypeOf(explicit).toEqualTypeOf(); + expectTypeOf(explicit).not.toEqualTypeOf>(); + }); - expectTypeOf(explicitPartialDataIgnore).toEqualTypeOf< - DeepPartial | undefined - >(); - expectTypeOf( - explicitPartialDataIgnore - ).not.toEqualTypeOf(); + it("handles combinations of options", () => { + const { query } = setupVariablesCase(); - const [inferredPartialDataNoneQueryRef] = useBackgroundQuery(query, { - returnPartialData: true, - errorPolicy: "none", - }); + const [inferredPartialDataIgnoreQueryRef] = useBackgroundQuery(query, { + returnPartialData: true, + errorPolicy: "ignore", + }); + const { data: inferredPartialDataIgnore } = useReadQuery( + inferredPartialDataIgnoreQueryRef + ); - const { data: inferredPartialDataNone } = useReadQuery( - inferredPartialDataNoneQueryRef - ); + expectTypeOf(inferredPartialDataIgnore).toEqualTypeOf< + DeepPartial | undefined + >(); + expectTypeOf( + inferredPartialDataIgnore + ).not.toEqualTypeOf(); + + const [explicitPartialDataIgnoreQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + returnPartialData: true, + errorPolicy: "ignore", + }); - expectTypeOf(inferredPartialDataNone).toEqualTypeOf< - DeepPartial - >(); - expectTypeOf( - inferredPartialDataNone - ).not.toEqualTypeOf(); - - const [explicitPartialDataNoneQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - returnPartialData: true, - errorPolicy: "none", - }); + const { data: explicitPartialDataIgnore } = useReadQuery( + explicitPartialDataIgnoreQueryRef + ); - const { data: explicitPartialDataNone } = useReadQuery( - explicitPartialDataNoneQueryRef - ); + expectTypeOf(explicitPartialDataIgnore).toEqualTypeOf< + DeepPartial | undefined + >(); + expectTypeOf( + explicitPartialDataIgnore + ).not.toEqualTypeOf(); - expectTypeOf(explicitPartialDataNone).toEqualTypeOf< - DeepPartial - >(); - expectTypeOf( - explicitPartialDataNone - ).not.toEqualTypeOf(); + const [inferredPartialDataNoneQueryRef] = useBackgroundQuery(query, { + returnPartialData: true, + errorPolicy: "none", }); - it("returns correct TData type when combined options that do not affect TData", () => { - const { query } = useVariablesIntegrationTestCase(); + const { data: inferredPartialDataNone } = useReadQuery( + inferredPartialDataNoneQueryRef + ); - const [inferredQueryRef] = useBackgroundQuery(query, { - fetchPolicy: "no-cache", - returnPartialData: true, - errorPolicy: "none", - }); - const { data: inferred } = useReadQuery(inferredQueryRef); + expectTypeOf(inferredPartialDataNone).toEqualTypeOf< + DeepPartial + >(); + expectTypeOf( + inferredPartialDataNone + ).not.toEqualTypeOf(); + + const [explicitPartialDataNoneQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + returnPartialData: true, + errorPolicy: "none", + }); - expectTypeOf(inferred).toEqualTypeOf>(); - expectTypeOf(inferred).not.toEqualTypeOf(); + const { data: explicitPartialDataNone } = useReadQuery( + explicitPartialDataNoneQueryRef + ); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { - fetchPolicy: "no-cache", - returnPartialData: true, - errorPolicy: "none", - }); + expectTypeOf(explicitPartialDataNone).toEqualTypeOf< + DeepPartial + >(); + expectTypeOf( + explicitPartialDataNone + ).not.toEqualTypeOf(); + }); - const { data: explicit } = useReadQuery(explicitQueryRef); + it("returns correct TData type when combined options that do not affect TData", () => { + const { query } = setupVariablesCase(); - expectTypeOf(explicit).toEqualTypeOf>(); - expectTypeOf(explicit).not.toEqualTypeOf(); + const [inferredQueryRef] = useBackgroundQuery(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + errorPolicy: "none", + }); + const { data: inferred } = useReadQuery(inferredQueryRef); + + expectTypeOf(inferred).toEqualTypeOf>(); + expectTypeOf(inferred).not.toEqualTypeOf(); + + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + errorPolicy: "none", }); - it("returns QueryReference | undefined when `skip` is present", () => { - const { query } = useVariablesIntegrationTestCase(); + const { data: explicit } = useReadQuery(explicitQueryRef); - const [inferredQueryRef] = useBackgroundQuery(query, { - skip: true, - }); + expectTypeOf(explicit).toEqualTypeOf>(); + expectTypeOf(explicit).not.toEqualTypeOf(); + }); - expectTypeOf(inferredQueryRef).toEqualTypeOf< - QueryReference | undefined - >(); - expectTypeOf(inferredQueryRef).not.toEqualTypeOf< - QueryReference - >(); - - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, { skip: true }); - - expectTypeOf(explicitQueryRef).toEqualTypeOf< - QueryReference | undefined - >(); - expectTypeOf(explicitQueryRef).not.toEqualTypeOf< - QueryReference - >(); - - // TypeScript is too smart and using a `const` or `let` boolean variable - // for the `skip` option results in a false positive. Using an options - // object allows us to properly check for a dynamic case. - const options = { - skip: true, - }; + it("returns QueryRef | undefined when `skip` is present", () => { + const { query } = setupVariablesCase(); - const [dynamicQueryRef] = useBackgroundQuery(query, { - skip: options.skip, - }); + const [inferredQueryRef] = useBackgroundQuery(query, { + skip: true, + }); + + expectTypeOf(inferredQueryRef).toEqualTypeOf< + QueryRef | undefined + >(); + expectTypeOf(inferredQueryRef).toMatchTypeOf< + QueryReference | undefined + >(); + expectTypeOf(inferredQueryRef).not.toEqualTypeOf< + QueryRef + >(); + + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { skip: true }); + + expectTypeOf(explicitQueryRef).toEqualTypeOf< + QueryRef | undefined + >(); + expectTypeOf(explicitQueryRef).toMatchTypeOf< + QueryReference | undefined + >(); + expectTypeOf(explicitQueryRef).not.toEqualTypeOf< + QueryRef + >(); + + // TypeScript is too smart and using a `const` or `let` boolean variable + // for the `skip` option results in a false positive. Using an options + // object allows us to properly check for a dynamic case. + const options = { + skip: true, + }; - expectTypeOf(dynamicQueryRef).toEqualTypeOf< - QueryReference | undefined - >(); - expectTypeOf(dynamicQueryRef).not.toEqualTypeOf< - QueryReference - >(); + const [dynamicQueryRef] = useBackgroundQuery(query, { + skip: options.skip, }); - it("returns `undefined` when using `skipToken` unconditionally", () => { - const { query } = useVariablesIntegrationTestCase(); + expectTypeOf(dynamicQueryRef).toEqualTypeOf< + QueryRef | undefined + >(); + expectTypeOf(dynamicQueryRef).toMatchTypeOf< + QueryReference | undefined + >(); + expectTypeOf(dynamicQueryRef).not.toEqualTypeOf< + QueryRef + >(); + }); - const [inferredQueryRef] = useBackgroundQuery(query, skipToken); + it("returns `undefined` when using `skipToken` unconditionally", () => { + const { query } = setupVariablesCase(); - expectTypeOf(inferredQueryRef).toEqualTypeOf(); - expectTypeOf(inferredQueryRef).not.toEqualTypeOf< - QueryReference | undefined - >(); + const [inferredQueryRef] = useBackgroundQuery(query, skipToken); - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, skipToken); + expectTypeOf(inferredQueryRef).toEqualTypeOf(); + expectTypeOf(inferredQueryRef).not.toEqualTypeOf< + QueryRef | undefined + >(); - expectTypeOf(explicitQueryRef).toEqualTypeOf(); - expectTypeOf(explicitQueryRef).not.toEqualTypeOf< - QueryReference | undefined - >(); - }); + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, skipToken); - it("returns QueryReference | undefined when using conditional `skipToken`", () => { - const { query } = useVariablesIntegrationTestCase(); - const options = { - skip: true, - }; + expectTypeOf(explicitQueryRef).toEqualTypeOf(); + expectTypeOf(explicitQueryRef).not.toEqualTypeOf< + QueryRef | undefined + >(); + }); - const [inferredQueryRef] = useBackgroundQuery( - query, - options.skip ? skipToken : undefined - ); + it("returns QueryRef | undefined when using conditional `skipToken`", () => { + const { query } = setupVariablesCase(); + const options = { + skip: true, + }; - expectTypeOf(inferredQueryRef).toEqualTypeOf< - QueryReference | undefined - >(); - expectTypeOf(inferredQueryRef).not.toEqualTypeOf< - QueryReference - >(); - - const [explicitQueryRef] = useBackgroundQuery< - VariablesCaseData, - VariablesCaseVariables - >(query, options.skip ? skipToken : undefined); - - expectTypeOf(explicitQueryRef).toEqualTypeOf< - QueryReference | undefined - >(); - expectTypeOf(explicitQueryRef).not.toEqualTypeOf< - QueryReference - >(); - }); - - it("returns QueryReference> | undefined when using `skipToken` with `returnPartialData`", () => { - const { query } = useVariablesIntegrationTestCase(); - const options = { - skip: true, - }; + const [inferredQueryRef] = useBackgroundQuery( + query, + options.skip ? skipToken : undefined + ); - const [inferredQueryRef] = useBackgroundQuery( - query, - options.skip ? skipToken : { returnPartialData: true } - ); + expectTypeOf(inferredQueryRef).toEqualTypeOf< + QueryRef | undefined + >(); + expectTypeOf(inferredQueryRef).toMatchTypeOf< + QueryReference | undefined + >(); + expectTypeOf(inferredQueryRef).not.toEqualTypeOf< + QueryRef + >(); + + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, options.skip ? skipToken : undefined); + + expectTypeOf(explicitQueryRef).toEqualTypeOf< + QueryRef | undefined + >(); + expectTypeOf(explicitQueryRef).toMatchTypeOf< + QueryReference | undefined + >(); + expectTypeOf(explicitQueryRef).not.toEqualTypeOf< + QueryRef + >(); + }); - expectTypeOf(inferredQueryRef).toEqualTypeOf< - QueryReference> | undefined - >(); - expectTypeOf(inferredQueryRef).not.toEqualTypeOf< - QueryReference - >(); + it("returns QueryRef> | undefined when using `skipToken` with `returnPartialData`", () => { + const { query } = setupVariablesCase(); + const options = { + skip: true, + }; - const [explicitQueryRef] = useBackgroundQuery( - query, - options.skip ? skipToken : { returnPartialData: true } - ); + const [inferredQueryRef] = useBackgroundQuery( + query, + options.skip ? skipToken : { returnPartialData: true } + ); - expectTypeOf(explicitQueryRef).toEqualTypeOf< - QueryReference> | undefined - >(); - expectTypeOf(explicitQueryRef).not.toEqualTypeOf< - QueryReference - >(); - }); + expectTypeOf(inferredQueryRef).toEqualTypeOf< + | QueryRef, VariablesCaseVariables> + | undefined + >(); + expectTypeOf(inferredQueryRef).toMatchTypeOf< + | QueryReference, VariablesCaseVariables> + | undefined + >(); + expectTypeOf(inferredQueryRef).not.toEqualTypeOf< + QueryRef + >(); + + const [explicitQueryRef] = useBackgroundQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, options.skip ? skipToken : { returnPartialData: true }); + + expectTypeOf(explicitQueryRef).toEqualTypeOf< + | QueryRef, VariablesCaseVariables> + | undefined + >(); + expectTypeOf(explicitQueryRef).toMatchTypeOf< + | QueryReference, VariablesCaseVariables> + | undefined + >(); + expectTypeOf(explicitQueryRef).not.toEqualTypeOf< + QueryRef + >(); }); }); diff --git a/src/react/hooks/__tests__/useFragment.test.tsx b/src/react/hooks/__tests__/useFragment.test.tsx index 6ef04ad4d01..f58ef9aaa6d 100644 --- a/src/react/hooks/__tests__/useFragment.test.tsx +++ b/src/react/hooks/__tests__/useFragment.test.tsx @@ -7,7 +7,7 @@ import { within, } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { act } from "react-dom/test-utils"; +import { act } from "@testing-library/react"; import { UseFragmentOptions, useFragment } from "../useFragment"; import { MockedProvider } from "../../../testing"; @@ -29,7 +29,7 @@ import { concatPagination } from "../../../utilities"; import assert from "assert"; import { expectTypeOf } from "expect-type"; import { SubscriptionObserver } from "zen-observable-ts"; -import { profile, spyOnConsole } from "../../../testing/internal"; +import { profile, profileHook, spyOnConsole } from "../../../testing/internal"; describe("useFragment", () => { it("is importable and callable", () => { @@ -333,6 +333,55 @@ describe("useFragment", () => { screen.getByText(/Item #1/); }); + it("allows the client to be overriden", () => { + const ItemFragment: TypedDocumentNode = gql` + fragment ItemFragment on Item { + id + text + } + `; + const cache = new InMemoryCache(); + const item = { __typename: "Item", id: 1, text: "Item #1" }; + cache.writeFragment({ + fragment: ItemFragment, + data: item, + }); + const client = new ApolloClient({ + cache, + }); + function Component() { + const { data } = useFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 1 }, + client, + }); + return <>{data.text}; + } + + // Without a MockedProvider supplying the client via context, + // the client must be passed directly to the hook or an error is thrown + expect(() => render()).not.toThrow(/pass an ApolloClient/); + + // Item #1 is rendered + screen.getByText(/Item #1/); + }); + + it("throws if no client is provided", () => { + function Component() { + const { data } = useFragment({ + fragment: ItemFragment, + from: { __typename: "Item", id: 1 }, + }); + return <>{data.text}; + } + + // silence the console error + { + using _spy = spyOnConsole("error"); + expect(() => render()).toThrow(/pass an ApolloClient/); + } + }); + it.each>([ // This query uses a basic field-level @nonreactive directive. gql` @@ -1231,29 +1280,29 @@ describe("useFragment", () => { from: { __typename: "Query" }, }); - return complete ? ( - <> - -
- -
-
    - {data.list.map((item) => ( - - ))} -
- - ) : null; + return complete ? + <> + +
+ +
+
    + {data.list.map((item) => ( + + ))} +
+ + : null; } function Item({ id }: { id: number }) { @@ -1310,6 +1359,205 @@ describe("useFragment", () => { }); }); + it("returns correct data when options change", async () => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + }); + type User = { __typename: "User"; id: number; name: string }; + const fragment: TypedDocumentNode = gql` + fragment UserFragment on User { + id + name + } + `; + + client.writeFragment({ + fragment, + data: { __typename: "User", id: 1, name: "Alice" }, + }); + + client.writeFragment({ + fragment, + data: { __typename: "User", id: 2, name: "Charlie" }, + }); + + const ProfiledHook = profileHook(({ id }: { id: number }) => + useFragment({ fragment, from: { __typename: "User", id } }) + ); + + const { rerender } = render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot).toEqual({ + complete: true, + data: { __typename: "User", id: 1, name: "Alice" }, + }); + } + + rerender(); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot).toEqual({ + complete: true, + data: { __typename: "User", id: 2, name: "Charlie" }, + }); + } + + await expect(ProfiledHook).not.toRerender(); + }); + + it("does not rerender when fields with @nonreactive change", async () => { + type Post = { + __typename: "User"; + id: number; + title: string; + updatedAt: string; + }; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + }); + + const fragment: TypedDocumentNode = gql` + fragment PostFragment on Post { + id + title + updatedAt @nonreactive + } + `; + + client.writeFragment({ + fragment, + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-01-01", + }, + }); + + const ProfiledHook = profileHook(() => + useFragment({ fragment, from: { __typename: "Post", id: 1 } }) + ); + + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot).toEqual({ + complete: true, + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-01-01", + }, + }); + } + + client.writeFragment({ + fragment, + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-02-01", + }, + }); + + await expect(ProfiledHook).not.toRerender(); + }); + + it("does not rerender when fields with @nonreactive on nested fragment change", async () => { + type Post = { + __typename: "User"; + id: number; + title: string; + updatedAt: string; + }; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + }); + + const fragment: TypedDocumentNode = gql` + fragment PostFragment on Post { + id + title + ...PostFields @nonreactive + } + + fragment PostFields on Post { + updatedAt + } + `; + + client.writeFragment({ + fragment, + fragmentName: "PostFragment", + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-01-01", + }, + }); + + const ProfiledHook = profileHook(() => + useFragment({ + fragment, + fragmentName: "PostFragment", + from: { __typename: "Post", id: 1 }, + }) + ); + + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot).toEqual({ + complete: true, + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-01-01", + }, + }); + } + + client.writeFragment({ + fragment, + fragmentName: "PostFragment", + data: { + __typename: "Post", + id: 1, + title: "Blog post", + updatedAt: "2024-02-01", + }, + }); + + await expect(ProfiledHook).not.toRerender(); + }); + describe("tests with incomplete data", () => { let cache: InMemoryCache, wrapper: React.FunctionComponent; const ItemFragment = gql` @@ -1476,7 +1724,7 @@ describe("has the same timing as `useQuery`", () => { from: initialItem, }); - ProfiledComponent.updateSnapshot({ queryData, fragmentData }); + ProfiledComponent.replaceSnapshot({ queryData, fragmentData }); return complete ? JSON.stringify(fragmentData) : "loading"; } @@ -1513,12 +1761,6 @@ describe("has the same timing as `useQuery`", () => { cache.writeQuery({ query, data: { item: updatedItem } }); - if (React.version.startsWith("17.")) { - const { snapshot } = await ProfiledComponent.takeRender(); - expect(snapshot.queryData).toStrictEqual({ item: initialItem }); - expect(snapshot.fragmentData).toStrictEqual(updatedItem); - } - { const { snapshot } = await ProfiledComponent.takeRender(); expect(snapshot.queryData).toStrictEqual({ item: updatedItem }); @@ -1727,6 +1969,7 @@ describe.skip("Type Tests", () => { optimistic?: boolean; variables?: TVars; canonizeResults?: boolean; + client?: ApolloClient; }>(); }); }); diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx index a36c0725bea..34981cf9b4e 100644 --- a/src/react/hooks/__tests__/useLazyQuery.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -5,6 +5,7 @@ import { act, render, renderHook, waitFor } from "@testing-library/react"; import { ApolloClient, + ApolloError, ApolloLink, ErrorPolicy, InMemoryCache, @@ -19,6 +20,7 @@ import { wait, tick, MockSubscriptionLink, + MockLink, } from "../../../testing"; import { useLazyQuery } from "../useLazyQuery"; import { QueryResult } from "../../types/types"; @@ -1115,7 +1117,7 @@ describe("useLazyQuery Hook", () => { ), }); - const [execute] = ProfiledHook.getCurrentSnapshot(); + const [execute] = await ProfiledHook.peekSnapshot(); { const [, result] = await ProfiledHook.takeSnapshot(); @@ -1483,6 +1485,274 @@ describe("useLazyQuery Hook", () => { expect(fetchCount).toBe(1); }); + // https://github.com/apollographql/apollo-client/issues/9448 + it.each(["network-only", "no-cache", "cache-and-network"] as const)( + "does not issue multiple network calls when calling execute again without variables with a %s fetch policy", + async (fetchPolicy) => { + interface Data { + user: { id: string; name: string }; + } + + interface Variables { + id?: string; + } + + const query: TypedDocumentNode = gql` + query UserQuery($id: ID) { + user(id: $id) { + id + name + } + } + `; + + let fetchCount = 0; + + const link = new ApolloLink((operation) => { + fetchCount++; + return new Observable((observer) => { + const { id } = operation.variables; + + setTimeout(() => { + observer.next({ + data: { + user: + id ? + { id, name: "John Doe" } + : { id: null, name: "John Default" }, + }, + }); + observer.complete(); + }, 20); + }); + }); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const { result } = renderHook( + () => useLazyQuery(query, { fetchPolicy }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + await act(() => result.current[0]({ variables: { id: "2" } })); + + expect(fetchCount).toBe(1); + + await waitFor(() => { + expect(result.current[1].data).toEqual({ + user: { id: "2", name: "John Doe" }, + }); + }); + + expect(fetchCount).toBe(1); + + await act(() => result.current[0]()); + + await waitFor(() => { + expect(result.current[1].data).toEqual({ + user: { id: null, name: "John Default" }, + }); + }); + + expect(fetchCount).toBe(2); + } + ); + + it("maintains stable execute function when passing in dynamic function options", async () => { + interface Data { + user: { id: string; name: string }; + } + + interface Variables { + id: string; + } + + const query: TypedDocumentNode = gql` + query UserQuery($id: ID!) { + user(id: $id) { + id + name + } + } + `; + + const link = new MockLink([ + { + request: { query, variables: { id: "1" } }, + result: { data: { user: { id: "1", name: "John Doe" } } }, + delay: 20, + }, + { + request: { query, variables: { id: "2" } }, + result: { errors: [new GraphQLError("Oops")] }, + delay: 20, + }, + { + request: { query, variables: { id: "3" } }, + result: { data: { user: { id: "3", name: "Johnny Three" } } }, + delay: 20, + maxUsageCount: Number.POSITIVE_INFINITY, + }, + ]); + + const client = new ApolloClient({ link, cache: new InMemoryCache() }); + + let countRef = { current: 0 }; + + const trackClosureValue = jest.fn(); + + const { result, rerender } = renderHook( + () => { + let count = countRef.current; + + return useLazyQuery(query, { + fetchPolicy: "cache-first", + variables: { id: "1" }, + onCompleted: () => { + trackClosureValue("onCompleted", count); + }, + onError: () => { + trackClosureValue("onError", count); + }, + skipPollAttempt: () => { + trackClosureValue("skipPollAttempt", count); + return false; + }, + nextFetchPolicy: (currentFetchPolicy) => { + trackClosureValue("nextFetchPolicy", count); + return currentFetchPolicy; + }, + }); + }, + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + const [originalExecute] = result.current; + + countRef.current++; + rerender(); + + expect(result.current[0]).toBe(originalExecute); + + // Check for stale closures with onCompleted + await act(() => result.current[0]()); + await waitFor(() => { + expect(result.current[1].data).toEqual({ + user: { id: "1", name: "John Doe" }, + }); + }); + + // after fetch + expect(trackClosureValue).toHaveBeenNthCalledWith(1, "nextFetchPolicy", 1); + expect(trackClosureValue).toHaveBeenNthCalledWith(2, "onCompleted", 1); + trackClosureValue.mockClear(); + + countRef.current++; + rerender(); + + expect(result.current[0]).toBe(originalExecute); + + // Check for stale closures with onError + await act(() => result.current[0]({ variables: { id: "2" } })); + await waitFor(() => { + expect(result.current[1].error).toEqual( + new ApolloError({ graphQLErrors: [new GraphQLError("Oops")] }) + ); + }); + + // variables changed + expect(trackClosureValue).toHaveBeenNthCalledWith(1, "nextFetchPolicy", 2); + // after fetch + expect(trackClosureValue).toHaveBeenNthCalledWith(2, "nextFetchPolicy", 2); + expect(trackClosureValue).toHaveBeenNthCalledWith(3, "onError", 2); + trackClosureValue.mockClear(); + + countRef.current++; + rerender(); + + expect(result.current[0]).toBe(originalExecute); + + await act(() => result.current[0]({ variables: { id: "3" } })); + await waitFor(() => { + expect(result.current[1].data).toEqual({ + user: { id: "3", name: "Johnny Three" }, + }); + }); + + // variables changed + expect(trackClosureValue).toHaveBeenNthCalledWith(1, "nextFetchPolicy", 3); + // after fetch + expect(trackClosureValue).toHaveBeenNthCalledWith(2, "nextFetchPolicy", 3); + expect(trackClosureValue).toHaveBeenNthCalledWith(3, "onCompleted", 3); + trackClosureValue.mockClear(); + + // Test for stale closures for skipPollAttempt + result.current[1].startPolling(20); + await wait(50); + result.current[1].stopPolling(); + + expect(trackClosureValue).toHaveBeenCalledWith("skipPollAttempt", 3); + }); + + it("maintains stable execute function identity when changing non-callback options", async () => { + interface Data { + user: { id: string; name: string }; + } + + interface Variables { + id: string; + } + + const query: TypedDocumentNode = gql` + query UserQuery($id: ID!) { + user(id: $id) { + id + name + } + } + `; + + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + setTimeout(() => { + observer.next({ + data: { user: { id: operation.variables.id, name: "John Doe" } }, + }); + observer.complete(); + }, 20); + }); + }); + + const client = new ApolloClient({ link, cache: new InMemoryCache() }); + + const { result, rerender } = renderHook( + ({ id }) => useLazyQuery(query, { variables: { id } }), + { + initialProps: { id: "1" }, + wrapper: ({ children }) => ( + {children} + ), + } + ); + + const [execute] = result.current; + + rerender({ id: "2" }); + + expect(result.current[0]).toBe(execute); + }); + describe("network errors", () => { async function check(errorPolicy: ErrorPolicy) { const networkError = new Error("from the network"); diff --git a/src/react/hooks/__tests__/useLoadableQuery.test.tsx b/src/react/hooks/__tests__/useLoadableQuery.test.tsx new file mode 100644 index 00000000000..a5e97ca52e8 --- /dev/null +++ b/src/react/hooks/__tests__/useLoadableQuery.test.tsx @@ -0,0 +1,5101 @@ +import React, { Suspense, useState } from "react"; +import { + act, + render, + screen, + renderHook, + waitFor, + RenderOptions, +} from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary"; +import { expectTypeOf } from "expect-type"; +import { GraphQLError } from "graphql"; +import { + gql, + ApolloError, + ApolloClient, + ErrorPolicy, + NetworkStatus, + TypedDocumentNode, + ApolloLink, + Observable, + OperationVariables, + RefetchWritePolicy, +} from "../../../core"; +import { + MockedProvider, + MockedProviderProps, + MockedResponse, + MockLink, + MockSubscriptionLink, + wait, +} from "../../../testing"; +import { + concatPagination, + offsetLimitPagination, + DeepPartial, +} from "../../../utilities"; +import { useLoadableQuery } from "../useLoadableQuery"; +import type { UseReadQueryResult } from "../useReadQuery"; +import { useReadQuery } from "../useReadQuery"; +import { ApolloProvider } from "../../context"; +import { InMemoryCache } from "../../../cache"; +import { LoadableQueryHookFetchPolicy } from "../../types/types"; +import { QueryRef } from "../../../react"; +import { FetchMoreFunction, RefetchFunction } from "../useSuspenseQuery"; +import invariant, { InvariantError } from "ts-invariant"; +import { + Profiler, + SimpleCaseData, + createProfiler, + setupPaginatedCase, + setupSimpleCase, + spyOnConsole, + useTrackRenders, +} from "../../../testing/internal"; + +const IS_REACT_19 = React.version.startsWith("19"); + +afterEach(() => { + jest.useRealTimers(); +}); + +interface SimpleQueryData { + greeting: string; +} + +function useSimpleQueryCase() { + const query: TypedDocumentNode = gql` + query GreetingQuery { + greeting + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + delay: 10, + }, + ]; + + return { query, mocks }; +} + +interface VariablesCaseData { + character: { + id: string; + name: string; + }; +} + +interface VariablesCaseVariables { + id: string; +} + +function useVariablesQueryCase() { + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } + } + `; + const CHARACTERS = ["Spider-Man", "Black Widow", "Iron Man", "Hulk"]; + + const mocks: MockedResponse[] = [...CHARACTERS].map( + (name, index) => ({ + request: { query, variables: { id: String(index + 1) } }, + result: { data: { character: { id: String(index + 1), name } } }, + delay: 20, + }) + ); + + return { mocks, query }; +} + +interface PaginatedQueryData { + letters: { + letter: string; + position: number; + }[]; +} + +interface PaginatedQueryVariables { + limit?: number; + offset?: number; +} + +function usePaginatedQueryCase() { + const query: TypedDocumentNode = + gql` + query letters($limit: Int, $offset: Int) { + letters(limit: $limit) { + letter + position + } + } + `; + + const data = "ABCDEFG" + .split("") + .map((letter, index) => ({ letter, position: index + 1 })); + + const link = new ApolloLink((operation) => { + const { offset = 0, limit = 2 } = operation.variables; + const letters = data.slice(offset, offset + limit); + + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { letters } }); + observer.complete(); + }, 10); + }); + }); + + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + + return { query, link, client }; +} + +function createDefaultProfiler() { + return createProfiler({ + initialSnapshot: { + error: null as Error | null, + result: null as UseReadQueryResult | null, + }, + skipNonTrackingRenders: true, + }); +} + +function createDefaultProfiledComponents< + Snapshot extends { + result: UseReadQueryResult | null; + error?: Error | null; + }, + TData = Snapshot["result"] extends UseReadQueryResult | null ? + TData + : unknown, +>(profiler: Profiler) { + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ queryRef }: { queryRef: QueryRef }) { + useTrackRenders(); + profiler.mergeSnapshot({ + result: useReadQuery(queryRef), + } as Partial); + + return null; + } + + function ErrorFallback({ error }: { error: Error }) { + useTrackRenders(); + profiler.mergeSnapshot({ error } as Partial); + + return
Oops
; + } + + function ErrorBoundary({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); + } + + return { + SuspenseFallback, + ReadQueryHook, + ErrorFallback, + ErrorBoundary, + }; +} + +function renderWithMocks( + ui: React.ReactElement, + { + wrapper: Wrapper = React.Fragment, + ...props + }: MockedProviderProps & { wrapper?: RenderOptions["wrapper"] } +) { + const user = userEvent.setup(); + + const utils = render(ui, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + return { ...utils, user }; +} + +function renderWithClient( + ui: React.ReactElement, + options: { client: ApolloClient; wrapper?: RenderOptions["wrapper"] } +) { + const { client, wrapper: Wrapper = React.Fragment } = options; + const user = userEvent.setup(); + + const utils = render(ui, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + return { ...utils, user }; +} + +it("loads a query and suspends when the load query function is called", async () => { + const { query, mocks } = useSimpleQueryCase(); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + } + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + } +}); + +it("loads a query with variables and suspends by passing variables to the loadQuery function", async () => { + const { query, mocks } = useVariablesQueryCase(); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + } + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("tears down the query on unmount", async () => { + const { query, mocks } = useSimpleQueryCase(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user, unmount } = renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + unmount(); + + // We need to wait a tick since the cleanup is run in a setTimeout to + // prevent strict mode bugs. + await wait(0); + + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); + +it("auto disposes of the queryRef if not used within timeout", async () => { + jest.useFakeTimers(); + const { query } = setupSimpleCase(); + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ link, cache: new InMemoryCache() }); + + const { result } = renderHook(() => useLoadableQuery(query, { client })); + const [loadQuery] = result.current; + + act(() => loadQuery()); + const [, queryRef] = result.current; + + expect(queryRef!).not.toBeDisposed(); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + await act(async () => { + link.simulateResult({ result: { data: { greeting: "Hello" } } }, true); + // Ensure simulateResult will deliver the result since its wrapped with + // setTimeout + await jest.advanceTimersByTimeAsync(10); + }); + + jest.advanceTimersByTime(30_000); + + expect(queryRef!).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); + +it("auto disposes of the queryRef if not used within configured timeout", async () => { + jest.useFakeTimers(); + const { query } = setupSimpleCase(); + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + defaultOptions: { + react: { + suspense: { + autoDisposeTimeoutMs: 5000, + }, + }, + }, + }); + + const { result } = renderHook(() => useLoadableQuery(query, { client })); + const [loadQuery] = result.current; + + act(() => loadQuery()); + const [, queryRef] = result.current; + + expect(queryRef!).not.toBeDisposed(); + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + await act(async () => { + link.simulateResult({ result: { data: { greeting: "Hello" } } }, true); + // Ensure simulateResult will deliver the result since its wrapped with + // setTimeout + await jest.advanceTimersByTimeAsync(10); + }); + + jest.advanceTimersByTime(5000); + + expect(queryRef!).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); + +it("will resubscribe after disposed when mounting useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + defaultOptions: { + react: { + suspense: { + // Set this to something really low to avoid fake timers + autoDisposeTimeoutMs: 20, + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(false); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + + }> + {show && queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + await act(() => user.click(screen.getByText("Load query"))); + + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + } + + // Wait long enough for auto dispose to kick in + await wait(50); + + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); + + await act(() => user.click(screen.getByText("Toggle"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "Hello again" }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it("auto resubscribes when mounting useReadQuery after naturally disposed by useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + + }> + {show && queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + await act(() => user.click(screen.getByText("Load query"))); + + expect(client.getObservableQueries().size).toBe(1); + expect(client).toHaveSuspenseCacheEntryUsing(query); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); + + await act(() => user.click(toggleButton)); + + expect(client.getObservableQueries().size).toBe(1); + // Here we don't expect a suspense cache entry because we previously disposed + // of it and did not call `loadQuery` again, which would normally add it to + // the suspense cache + expect(client).not.toHaveSuspenseCacheEntryUsing(query); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "Hello again" }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + +it("changes variables on a query and resuspends when passing new variables to the loadQuery function", async () => { + const { query, mocks } = useVariablesQueryCase(); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const App = () => { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + }; + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + } + + await act(() => user.click(screen.getByText("Load 1st character"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + } + + await act(() => user.click(screen.getByText("Load 2nd character"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "2", name: "Black Widow" } }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("resets the `queryRef` to null and disposes of it when calling the `reset` function", async () => { + const { query, mocks } = useSimpleQueryCase(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { reset }] = useLoadableQuery(query); + + // Resetting the result allows us to detect when ReadQueryHook is unmounted + // since it won't render and overwrite the `null` + Profiler.mergeSnapshot({ result: null }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Reset query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot.result).toBeNull(); + } + + // Since dispose is called in a setTimeout, we need to wait a tick before + // checking to see if the query ref was properly disposed + await wait(0); + + expect(client.getObservableQueries().size).toBe(0); +}); + +it("allows the client to be overridden", async () => { + const { query } = useSimpleQueryCase(); + + const globalClient = new ApolloClient({ + link: new MockLink([ + { + request: { query }, + result: { data: { greeting: "global hello" } }, + delay: 10, + }, + ]), + cache: new InMemoryCache(), + }); + + const localClient = new ApolloClient({ + link: new MockLink([ + { + request: { query }, + result: { data: { greeting: "local hello" } }, + delay: 10, + }, + ]), + cache: new InMemoryCache(), + }); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + client: localClient, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { + client: globalClient, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "local hello" }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); +}); + +it("passes context to the link", async () => { + interface QueryData { + context: Record; + } + + const query: TypedDocumentNode = gql` + query ContextQuery { + context + } + `; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink((operation) => { + return new Observable((observer) => { + const { valueA, valueB } = operation.getContext(); + setTimeout(() => { + observer.next({ data: { context: { valueA, valueB } } }); + observer.complete(); + }, 10); + }); + }), + }); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + context: { valueA: "A", valueB: "B" }, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { context: { valueA: "A", valueB: "B" } }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); +}); + +it('enables canonical results when canonizeResults is "true"', async () => { + interface Result { + __typename: string; + value: number; + } + + interface QueryData { + results: Result[]; + } + + const cache = new InMemoryCache({ + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); + + const query: TypedDocumentNode = gql` + query { + results { + value + } + } + `; + + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; + + cache.writeQuery({ + query, + data: { results }, + }); + + const client = new ApolloClient({ + cache, + link: new MockLink([]), + }); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + canonizeResults: true, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + const { snapshot } = await Profiler.takeRender(); + const resultSet = new Set(snapshot.result?.data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(snapshot.result).toEqual({ + data: { results }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + + expect(resultSet.size).toBe(5); + expect(values).toEqual([0, 1, 2, 3, 5]); +}); + +it("can disable canonical results when the cache's canonizeResults setting is true", async () => { + interface Result { + __typename: string; + value: number; + } + + interface QueryData { + results: Result[]; + } + + const cache = new InMemoryCache({ + canonizeResults: true, + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); + + const query: TypedDocumentNode<{ results: Result[] }, never> = gql` + query { + results { + value + } + } + `; + + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; + + cache.writeQuery({ + query, + data: { results }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + canonizeResults: false, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { cache, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + const { snapshot } = await Profiler.takeRender(); + const resultSet = new Set(snapshot.result!.data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(snapshot.result).toEqual({ + data: { results }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + expect(resultSet.size).toBe(6); + expect(values).toEqual([0, 1, 1, 2, 3, 5]); +}); + +it("returns initial cache data followed by network data when the fetch policy is `cache-and-network`", async () => { + type QueryData = { hello: string }; + const query: TypedDocumentNode = gql` + query { + hello + } + `; + const cache = new InMemoryCache(); + const link = new MockLink([ + { + request: { query }, + result: { data: { hello: "from link" } }, + delay: 20, + }, + ]); + + const client = new ApolloClient({ link, cache }); + + cache.writeQuery({ query, data: { hello: "from cache" } }); + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "cache-and-network", + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { hello: "from cache" }, + networkStatus: NetworkStatus.loading, + error: undefined, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { hello: "from link" }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + } +}); + +it("all data is present in the cache, no network request is made", async () => { + const query = gql` + query { + hello + } + `; + const cache = new InMemoryCache(); + const link = new MockLink([ + { + request: { query }, + result: { data: { hello: "from link" } }, + delay: 20, + }, + ]); + + const client = new ApolloClient({ + link, + cache, + }); + + cache.writeQuery({ query, data: { hello: "from cache" } }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { hello: "from cache" }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + + await expect(Profiler).not.toRerender(); +}); + +it("partial data is present in the cache so it is ignored and network request is made", async () => { + const query = gql` + { + hello + foo + } + `; + const cache = new InMemoryCache(); + const link = new MockLink([ + { + request: { query }, + result: { data: { hello: "from link", foo: "bar" } }, + delay: 20, + }, + ]); + + const client = new ApolloClient({ + link, + cache, + }); + + { + // we expect a "Missing field 'foo' while writing result..." error + // when writing hello to the cache, so we'll silence the console.error + using _consoleSpy = spyOnConsole("error"); + cache.writeQuery({ query, data: { hello: "from cache" } }); + } + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { foo: "bar", hello: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it("existing data in the cache is ignored when `fetchPolicy` is 'network-only'", async () => { + const query = gql` + query { + hello + } + `; + const cache = new InMemoryCache(); + const link = new MockLink([ + { + request: { query }, + result: { data: { hello: "from link" } }, + delay: 20, + }, + ]); + + const client = new ApolloClient({ + link, + cache, + }); + + cache.writeQuery({ query, data: { hello: "from cache" } }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "network-only", + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { hello: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it("fetches data from the network but does not update the cache when `fetchPolicy` is 'no-cache'", async () => { + const query = gql` + query { + hello + } + `; + const cache = new InMemoryCache(); + const link = new MockLink([ + { + request: { query }, + result: { data: { hello: "from link" } }, + delay: 20, + }, + ]); + + const client = new ApolloClient({ link, cache }); + + cache.writeQuery({ query, data: { hello: "from cache" } }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "no-cache", + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { hello: "from link" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(client.extract()).toEqual({ + ROOT_QUERY: { __typename: "Query", hello: "from cache" }, + }); +}); + +it("works with startTransition to change variables", async () => { + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "2" } }, + result: { + data: { + todo: { id: "2", name: "Take out trash", completed: true }, + }, + }, + delay: 10, + }, + ]; + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); + + function SuspenseFallback() { + return

Loading

; + } + + function App() { + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( +
+ + }> + {queryRef && ( + loadQuery({ id })} /> + )} + +
+ ); + } + + function Todo({ + queryRef, + onChange, + }: { + queryRef: QueryRef; + onChange: (id: string) => void; + }) { + const { data } = useReadQuery(queryRef); + const [isPending, startTransition] = React.useTransition(); + const { todo } = data; + + return ( + <> + +
+ {todo.name} + {todo.completed && " (completed)"} +
+ + ); + } + + const { user } = renderWithClient(, { client }); + + await act(() => user.click(screen.getByText("Load first todo"))); + + expect(screen.getByText("Loading")).toBeInTheDocument(); + expect(await screen.findByTestId("todo")).toBeInTheDocument(); + + const todo = screen.getByTestId("todo"); + const button = screen.getByText("Refresh"); + + expect(todo).toHaveTextContent("Clean room"); + + await act(() => user.click(button)); + + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component suspends + // until the todo is finished loading. Seeing the suspense fallback is an + // indication that we are suspending the component too late in the process. + expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + + // We can ensure this works with isPending from useTransition in the process + expect(todo).toHaveAttribute("aria-busy", "true"); + + // Ensure we are showing the stale UI until the new todo has loaded + expect(todo).toHaveTextContent("Clean room"); + + // Eventually we should see the updated todo content once its done + // suspending. + await waitFor(() => { + expect(todo).toHaveTextContent("Take out trash (completed)"); + }); +}); + +it('does not suspend deferred queries with data in the cache and using a "cache-and-network" fetch policy', async () => { + interface Data { + greeting: { + __typename: string; + message: string; + recipient: { name: string; __typename: string }; + }; + } + + const query: TypedDocumentNode = gql` + query { + greeting { + message + ... @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + const cache = new InMemoryCache(); + cache.writeQuery({ + query, + data: { + greeting: { + __typename: "Greeting", + message: "Hello cached", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + }); + const client = new ApolloClient({ cache, link }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "cache-and-network", + }); + return ( +
+ + }> + {queryRef && } + +
+ ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load todo"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello cached", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + link.simulateResult({ + result: { + data: { + greeting: { __typename: "Greeting", message: "Hello world" }, + }, + hasNext: true, + }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + link.simulateResult( + { + result: { + incremental: [ + { + data: { + recipient: { name: "Alice", __typename: "Person" }, + __typename: "Greeting", + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("reacts to cache updates", async () => { + const { query, mocks } = useSimpleQueryCase(); + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "Updated Hello" }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Updated Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("applies `errorPolicy` on next fetch when it changes between renders", async () => { + const { query } = useSimpleQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + delay: 10, + }, + { + request: { query }, + result: { + errors: [new GraphQLError("oops")], + }, + delay: 10, + }, + ]; + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook, ErrorBoundary, ErrorFallback } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [errorPolicy, setErrorPolicy] = useState("none"); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + errorPolicy, + }); + + return ( + <> + + + + }> + + {queryRef && } + + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Change error policy"))); + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Refetch greeting"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + // Ensure we aren't rendering the error boundary and instead rendering the + // error message in the hook component. + expect(renderedComponents).not.toContain(ErrorFallback); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: new ApolloError({ graphQLErrors: [new GraphQLError("oops")] }), + networkStatus: NetworkStatus.error, + }); + } +}); + +it("applies `context` on next fetch when it changes between renders", async () => { + interface Data { + phase: string; + } + + const query: TypedDocumentNode = gql` + query { + phase + } + `; + + const link = new ApolloLink((operation) => { + return new Observable((subscriber) => { + setTimeout(() => { + subscriber.next({ + data: { + phase: operation.getContext().phase, + }, + }); + subscriber.complete(); + }, 10); + }); + }); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [phase, setPhase] = React.useState("initial"); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + context: { phase }, + }); + + return ( + <> + + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result!.data).toEqual({ + phase: "initial", + }); + } + + await act(() => user.click(screen.getByText("Update context"))); + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result!.data).toEqual({ + phase: "rerender", + }); + } +}); + +// NOTE: We only test the `false` -> `true` path here. If the option changes +// from `true` -> `false`, the data has already been canonized, so it has no +// effect on the output. +it("returns canonical results immediately when `canonizeResults` changes from `false` to `true` between renders", async () => { + interface Result { + __typename: string; + value: number; + } + + interface Data { + results: Result[]; + } + + const cache = new InMemoryCache({ + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); + + const query: TypedDocumentNode = gql` + query { + results { + value + } + } + `; + + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; + + cache.writeQuery({ + query, + data: { results }, + }); + + const client = new ApolloClient({ + link: new MockLink([]), + cache, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [canonizeResults, setCanonizeResults] = React.useState(false); + const [loadQuery, queryRef] = useLoadableQuery(query, { + canonizeResults, + }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot } = await Profiler.takeRender(); + const { data } = snapshot.result!; + const resultSet = new Set(data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(data.results.length).toBe(6); + expect(resultSet.size).toBe(6); + expect(values).toEqual([0, 1, 1, 2, 3, 5]); + } + + await act(() => user.click(screen.getByText("Canonize results"))); + + { + const { snapshot } = await Profiler.takeRender(); + const { data } = snapshot.result!; + const resultSet = new Set(data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(data.results.length).toBe(6); + expect(resultSet.size).toBe(5); + expect(values).toEqual([0, 1, 2, 3, 5]); + } +}); + +it("applies changed `refetchWritePolicy` to next fetch when changing between renders", async () => { + interface Data { + primes: number[]; + } + + const query: TypedDocumentNode = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + { + request: { query, variables: { min: 30, max: 50 } }, + result: { data: { primes: [31, 37, 41, 43, 47] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [refetchWritePolicy, setRefetchWritePolicy] = + React.useState("merge"); + + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + refetchWritePolicy, + }); + + return ( + <> + + + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + const { primes } = snapshot.result!.data; + + expect(primes).toEqual([2, 3, 5, 7, 11]); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch next"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + const { primes } = snapshot.result!.data; + + expect(primes).toEqual([2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + ]); + } + + await act(() => user.click(screen.getByText("Change refetch write policy"))); + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Refetch last"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + const { primes } = snapshot.result!.data; + + expect(primes).toEqual([31, 37, 41, 43, 47]); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + [undefined, [31, 37, 41, 43, 47]], + ]); + } +}); + +it("applies `returnPartialData` on next fetch when it changes between renders", async () => { + interface Data { + character: { + __typename: "Character"; + id: string; + name: string; + }; + } + + interface PartialData { + character: { + __typename: "Character"; + id: string; + }; + } + + const fullQuery: TypedDocumentNode = gql` + query { + character { + __typename + id + name + } + } + `; + + const partialQuery: TypedDocumentNode = gql` + query { + character { + __typename + id + } + } + `; + + const mocks = [ + { + request: { query: fullQuery }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange", + }, + }, + }, + delay: 10, + }, + { + request: { query: fullQuery }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange (refetched)", + }, + }, + }, + delay: 100, + }, + ]; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + }); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [returnPartialData, setReturnPartialData] = React.useState(false); + + const [loadQuery, queryRef] = useLoadableQuery(fullQuery, { + returnPartialData, + }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Doctor Strange" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Update partial data"))); + await Profiler.takeRender(); + + cache.modify({ + id: cache.identify({ __typename: "Character", id: "1" }), + fields: { + name: (_, { DELETE }) => DELETE, + }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1" }, + }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange (refetched)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it("applies updated `fetchPolicy` on next fetch when it changes between renders", async () => { + interface Data { + character: { + __typename: "Character"; + id: string; + name: string; + }; + } + + const query: TypedDocumentNode = gql` + query { + character { + __typename + id + name + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange", + }, + }, + }, + delay: 10, + }, + ]; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query, + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }, + }); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [fetchPolicy, setFetchPolicy] = + React.useState("cache-first"); + + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + fetchPolicy, + }); + + return ( + <> + + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Change fetch policy"))); + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Doctor Strange", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // Because we switched to a `no-cache` fetch policy, we should not see the + // newly fetched data in the cache after the fetch occured. + expect(cache.readQuery({ query })).toEqual({ + character: { + __typename: "Character", + id: "1", + name: "Doctor Strangecache", + }, + }); +}); + +it("re-suspends when calling `refetch`", async () => { + const { query } = useVariablesQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Spider-Man" } }, + }, + delay: 20, + }, + // refetch + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Spider-Man (updated)" } }, + }, + delay: 20, + }, + ]; + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man (updated)" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("re-suspends when calling `refetch` with new variables", async () => { + const { query } = useVariablesQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Captain Marvel" } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "2" } }, + result: { + data: { character: { id: "2", name: "Captain America" } }, + }, + delay: 10, + }, + ]; + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch with ID 2"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "2", name: "Captain America" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("re-suspends multiple times when calling `refetch` multiple times", async () => { + const { query } = useVariablesQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Spider-Man" } }, + }, + maxUsageCount: 3, + delay: 10, + }, + ]; + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + const button = screen.getByText("Refetch"); + + await act(() => user.click(button)); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(button)); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("throws errors when errors are returned after calling `refetch`", async () => { + using _consoleSpy = spyOnConsole("error"); + + const { query } = useVariablesQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Captain Marvel" } }, + }, + delay: 20, + }, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 20, + }, + ]; + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook, ErrorBoundary, ErrorFallback } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + + }> + + {queryRef && } + + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ErrorFallback]); + expect(snapshot.error).toEqual( + new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }) + ); + } +}); + +it('ignores errors returned after calling `refetch` when errorPolicy is set to "ignore"', async () => { + const { query } = useVariablesQueryCase(); + + const mocks = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Captain Marvel" } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 10, + }, + ]; + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook, ErrorBoundary, ErrorFallback } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + errorPolicy: "ignore", + }); + + return ( + <> + + + }> + + {queryRef && } + + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toStrictEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.error).toBeNull(); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + expect(renderedComponents).not.toContain(ErrorFallback); + } + + await expect(Profiler).not.toRerender(); +}); + +it('returns errors after calling `refetch` when errorPolicy is set to "all"', async () => { + const { query } = useVariablesQueryCase(); + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Captain Marvel" } }, + }, + delay: 20, + }, + { + request: { query, variables: { id: "1" } }, + result: { + errors: [new GraphQLError("Something went wrong")], + }, + delay: 20, + }, + ]; + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook, ErrorBoundary, ErrorFallback } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + errorPolicy: "all", + }); + + return ( + <> + + + }> + + {queryRef && } + + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).not.toContain(ErrorFallback); + expect(snapshot.error).toBeNull(); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }), + networkStatus: NetworkStatus.error, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it('handles partial data results after calling `refetch` when errorPolicy is set to "all"', async () => { + const { query } = useVariablesQueryCase(); + + const mocks = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: "Captain Marvel" } }, + }, + delay: 20, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { character: { id: "1", name: null } }, + errors: [new GraphQLError("Something went wrong")], + }, + delay: 20, + }, + ]; + + const Profiler = createDefaultProfiler(); + + const { SuspenseFallback, ReadQueryHook, ErrorBoundary, ErrorFallback } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + errorPolicy: "all", + }); + + return ( + <> + + + }> + + {queryRef && } + + + + ); + } + + const { user } = renderWithMocks(, { mocks, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Captain Marvel" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).not.toContain(ErrorFallback); + expect(snapshot.error).toBeNull(); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: null } }, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Something went wrong")], + }), + networkStatus: NetworkStatus.error, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("`refetch` works with startTransition to allow React to show stale UI until finished suspending", async () => { + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + }, + delay: 10, + }, + ]; + + function SuspenseFallback() { + return

Loading

; + } + + function App() { + const [id, setId] = React.useState("1"); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && ( + + )} + + + ); + } + + function Todo({ + queryRef, + refetch, + }: { + refetch: RefetchFunction; + queryRef: QueryRef; + onChange: (id: string) => void; + }) { + const { data } = useReadQuery(queryRef); + const [isPending, startTransition] = React.useTransition(); + const { todo } = data; + + return ( + <> + +
+ {todo.name} + {todo.completed && " (completed)"} +
+ + ); + } + + const { user } = renderWithMocks(, { mocks }); + + await act(() => user.click(screen.getByText("Load query"))); + + expect(screen.getByText("Loading")).toBeInTheDocument(); + expect(await screen.findByTestId("todo")).toBeInTheDocument(); + + const todo = screen.getByTestId("todo"); + const button = screen.getByText("Refresh"); + + expect(todo).toHaveTextContent("Clean room"); + + await act(() => user.click(button)); + + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component suspends + // until the todo is finished loading. Seeing the suspense fallback is an + // indication that we are suspending the component too late in the process. + expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + + // We can ensure this works with isPending from useTransition in the process + expect(todo).toHaveAttribute("aria-busy", "true"); + + // Ensure we are showing the stale UI until the new todo has loaded + expect(todo).toHaveTextContent("Clean room"); + + // Eventually we should see the updated todo content once its done + // suspending. + await waitFor(() => { + expect(todo).toHaveTextContent("Clean room (completed)"); + }); +}); + +it("re-suspends when calling `fetchMore` with different variables", async () => { + const { query, link } = setupPaginatedCase(); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { + keyArgs: false, + }, + }, + }, + }, + }), + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { fetchMore }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Fetch more"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("properly uses `updateQuery` when calling `fetchMore`", async () => { + const { query, client } = usePaginatedQueryCase(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { fetchMore }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { letter: "A", position: 1 }, + { letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Fetch more"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { letter: "A", position: 1 }, + { letter: "B", position: 2 }, + { letter: "C", position: 3 }, + { letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // TODO investigate: this test highlights a React render + // that actually doesn't rerender any user-provided components + // so we need to use `skipNonTrackingRenders` + await expect(Profiler).not.toRerender(); +}); + +it("properly uses cache field policies when calling `fetchMore` without `updateQuery`", async () => { + const { query, link } = usePaginatedQueryCase(); + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: concatPagination(), + }, + }, + }, + }), + }); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { fetchMore }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { letter: "A", position: 1 }, + { letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Fetch more"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { letter: "A", position: 1 }, + { letter: "B", position: 2 }, + { letter: "C", position: 3 }, + { letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // TODO investigate: this test highlights a React render + // that actually doesn't rerender any user-provided components + // so we need to use `skipNonTrackingRenders` + await expect(Profiler).not.toRerender(); +}); + +it("`fetchMore` works with startTransition to allow React to show stale UI until finished suspending", async () => { + type Variables = { + offset: number; + }; + + interface Todo { + __typename: "Todo"; + id: string; + name: string; + completed: boolean; + } + interface Data { + todos: Todo[]; + } + + const query: TypedDocumentNode = gql` + query TodosQuery($offset: Int!) { + todos(offset: $offset) { + id + name + completed + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { offset: 0 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, + }, + delay: 10, + }, + { + request: { query, variables: { offset: 1 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, + }, + delay: 10, + }, + ]; + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + todos: offsetLimitPagination(), + }, + }, + }, + }), + }); + + function SuspenseFallback() { + return

Loading

; + } + + function App() { + const [loadQuery, queryRef, { fetchMore }] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + function Todo({ + queryRef, + fetchMore, + }: { + fetchMore: FetchMoreFunction; + queryRef: QueryRef; + }) { + const { data } = useReadQuery(queryRef); + const [isPending, startTransition] = React.useTransition(); + const { todos } = data; + + return ( + <> + +
+ {todos.map((todo) => ( +
+ {todo.name} + {todo.completed && " (completed)"} +
+ ))} +
+ + ); + } + + const { user } = renderWithClient(, { client }); + + await act(() => user.click(screen.getByText("Load query"))); + + expect(screen.getByText("Loading")).toBeInTheDocument(); + + expect(await screen.findByTestId("todos")).toBeInTheDocument(); + + const todos = screen.getByTestId("todos"); + const todo1 = screen.getByTestId("todo:1"); + const button = screen.getByText("Load more"); + + expect(todo1).toBeInTheDocument(); + + await act(() => user.click(button)); + + // startTransition will avoid rendering the suspense fallback for already + // revealed content if the state update inside the transition causes the + // component to suspend. + // + // Here we should not see the suspense fallback while the component suspends + // until the todo is finished loading. Seeing the suspense fallback is an + // indication that we are suspending the component too late in the process. + expect(screen.queryByText("Loading")).not.toBeInTheDocument(); + + // We can ensure this works with isPending from useTransition in the process + expect(todos).toHaveAttribute("aria-busy", "true"); + + // Ensure we are showing the stale UI until the new todo has loaded + expect(todo1).toHaveTextContent("Clean room"); + + // Eventually we should see the updated todos content once its done + // suspending. + await waitFor(() => { + expect(screen.getByTestId("todo:2")).toHaveTextContent( + "Take out trash (completed)" + ); + expect(todo1).toHaveTextContent("Clean room"); + }); +}); + +it('honors refetchWritePolicy set to "merge"', async () => { + const query: TypedDocumentNode< + { primes: number[] }, + { min: number; max: number } + > = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query, { + refetchWritePolicy: "merge", + }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + ]); + } + + await expect(Profiler).not.toRerender(); +}); + +it('defaults refetchWritePolicy to "overwrite"', async () => { + const query: TypedDocumentNode< + { primes: number[] }, + { min: number; max: number } + > = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef, { refetch }] = useLoadableQuery(query); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial load + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [undefined, [13, 17, 19, 23, 29]], + ]); + } +}); + +it('does not suspend when partial data is in the cache and using a "cache-first" fetch policy with returnPartialData', async () => { + interface Data { + character: { + id: string; + name: string; + }; + } + + const fullQuery: TypedDocumentNode = gql` + query { + character { + id + name + } + } + `; + + const partialQuery = gql` + query { + character { + id + } + } + `; + const mocks = [ + { + request: { query: fullQuery }, + result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + delay: 20, + }, + ]; + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + }); + + const client = new ApolloClient({ link: new MockLink(mocks), cache }); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(fullQuery, { + fetchPolicy: "cache-first", + returnPartialData: true, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial load + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Doctor Strange" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + } + + await expect(Profiler).not.toRerender(); +}); + +it('suspends and does not use partial data from other variables in the cache when changing variables and using a "cache-first" fetch policy with returnPartialData: true', async () => { + const { query, mocks } = useVariablesQueryCase(); + + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id + } + } + `; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + variables: { id: "1" }, + }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "cache-first", + returnPartialData: true, + }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { + mocks, + cache, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + } + + await act(() => user.click(screen.getByText("Change variables"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "2", name: "Black Widow" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + } + + await expect(Profiler).not.toRerender(); +}); + +it('suspends when partial data is in the cache and using a "network-only" fetch policy with returnPartialData', async () => { + interface Data { + character: { + id: string; + name: string; + }; + } + + const fullQuery: TypedDocumentNode = gql` + query { + character { + id + name + } + } + `; + + const partialQuery = gql` + query { + character { + id + } + } + `; + const mocks = [ + { + request: { query: fullQuery }, + result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + delay: 10, + }, + ]; + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + }); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(fullQuery, { + fetchPolicy: "network-only", + returnPartialData: true, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { + mocks, + cache, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Doctor Strange" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it('suspends when partial data is in the cache and using a "no-cache" fetch policy with returnPartialData', async () => { + using _consoleSpy = spyOnConsole("warn"); + + interface Data { + character: { + id: string; + name: string; + }; + } + + const fullQuery: TypedDocumentNode = gql` + query { + character { + id + name + } + } + `; + + const partialQuery = gql` + query { + character { + id + } + } + `; + const mocks = [ + { + request: { query: fullQuery }, + result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + delay: 10, + }, + ]; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(fullQuery, { + fetchPolicy: "no-cache", + returnPartialData: true, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { + mocks, + cache, + wrapper: Profiler, + }); + + // initial load + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Doctor Strange" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it('warns when using returnPartialData with a "no-cache" fetch policy', async () => { + using _consoleSpy = spyOnConsole("warn"); + + const query: TypedDocumentNode = gql` + query UserQuery { + greeting + } + `; + + renderHook( + () => + useLoadableQuery(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + }), + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn).toHaveBeenCalledWith( + "Using `returnPartialData` with a `no-cache` fetch policy has no effect. To read partial data from the cache, consider using an alternate fetch policy." + ); +}); + +it('does not suspend when partial data is in the cache and using a "cache-and-network" fetch policy with returnPartialData', async () => { + interface Data { + character: { + id: string; + name: string; + }; + } + + const fullQuery: TypedDocumentNode = gql` + query { + character { + id + name + } + } + `; + + const partialQuery = gql` + query { + character { + id + } + } + `; + const mocks = [ + { + request: { query: fullQuery }, + result: { data: { character: { id: "1", name: "Doctor Strange" } } }, + delay: 20, + }, + ]; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(fullQuery, { + fetchPolicy: "cache-and-network", + returnPartialData: true, + }); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { + mocks, + cache, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Doctor Strange" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it('suspends and does not use partial data when changing variables and using a "cache-and-network" fetch policy with returnPartialData', async () => { + const { query, mocks } = useVariablesQueryCase(); + + const partialQuery = gql` + query ($id: ID!) { + character(id: $id) { + id + } + } + `; + + const cache = new InMemoryCache(); + + cache.writeQuery({ + query: partialQuery, + data: { character: { id: "1" } }, + variables: { id: "1" }, + }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query, { + fetchPolicy: "cache-and-network", + returnPartialData: true, + }); + + return ( + <> + + + }> + {queryRef && } + + + ); + } + + const { user } = renderWithMocks(, { + mocks, + cache, + wrapper: Profiler, + }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { id: "1", name: "Spider-Man" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Change variables"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { character: { id: "2", name: "Black Widow" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +it('does not suspend deferred queries with partial data in the cache and using a "cache-first" fetch policy with `returnPartialData`', async () => { + interface QueryData { + greeting: { + __typename: string; + message?: string; + recipient?: { + __typename: string; + name: string; + }; + }; + } + + const query: TypedDocumentNode = gql` + query { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + const cache = new InMemoryCache(); + + { + // We are intentionally writing partial data to the cache. Supress console + // warnings to avoid unnecessary noise in the test. + using _consoleSpy = spyOnConsole("error"); + + cache.writeQuery({ + query, + data: { + greeting: { + __typename: "Greeting", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + }); + } + + const client = new ApolloClient({ link, cache }); + + const Profiler = createDefaultProfiler>(); + const { SuspenseFallback, ReadQueryHook } = + createDefaultProfiledComponents(Profiler); + + function App() { + useTrackRenders(); + const [loadTodo, queryRef] = useLoadableQuery(query, { + fetchPolicy: "cache-first", + returnPartialData: true, + }); + + return ( +
+ + }> + {queryRef && } + +
+ ); + } + + const { user } = renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load todo"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + link.simulateResult({ + result: { + data: { + greeting: { message: "Hello world", __typename: "Greeting" }, + }, + hasNext: true, + }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Cached Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + link.simulateResult( + { + result: { + incremental: [ + { + data: { + __typename: "Greeting", + recipient: { name: "Alice", __typename: "Person" }, + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +it("throws when calling loadQuery on first render", async () => { + // We don't provide this functionality with React 19 anymore since it requires internals access + if (IS_REACT_19) return; + using _consoleSpy = spyOnConsole("error"); + const { query, mocks } = useSimpleQueryCase(); + + function App() { + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + + return null; + } + + expect(() => renderWithMocks(, { mocks })).toThrow( + new InvariantError( + "useLoadableQuery: 'loadQuery' should not be called during render. To start a query during render, use the 'useBackgroundQuery' hook." + ) + ); +}); + +it("throws when calling loadQuery on subsequent render", async () => { + // We don't provide this functionality with React 19 anymore since it requires internals access + if (React.version.startsWith("19")) return; + using _consoleSpy = spyOnConsole("error"); + const { query, mocks } = useSimpleQueryCase(); + + let error!: Error; + + function App() { + const [count, setCount] = useState(0); + const [loadQuery] = useLoadableQuery(query); + + if (count === 1) { + loadQuery(); + } + + return ; + } + + const { user } = renderWithMocks( + (error = e)} fallback={
Oops
}> + +
, + { mocks } + ); + + await act(() => user.click(screen.getByText("Load query in render"))); + + expect(error).toEqual( + new InvariantError( + "useLoadableQuery: 'loadQuery' should not be called during render. To start a query during render, use the 'useBackgroundQuery' hook." + ) + ); +}); + +it("allows loadQuery to be called in useEffect on first render", async () => { + const { query, mocks } = useSimpleQueryCase(); + + function App() { + const [loadQuery] = useLoadableQuery(query); + + React.useEffect(() => { + loadQuery(); + }, []); + + return null; + } + + expect(() => renderWithMocks(, { mocks })).not.toThrow(); +}); + +describe.skip("type tests", () => { + it("returns unknown when TData cannot be inferred", () => { + const query = gql``; + + const [, queryRef] = useLoadableQuery(query); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + }); + + it("variables are optional and can be anything with an untyped DocumentNode", () => { + const query = gql``; + + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + loadQuery({}); + loadQuery({ foo: "bar" }); + loadQuery({ bar: "baz" }); + }); + + it("variables are optional and can be anything with unspecified TVariables on a TypedDocumentNode", () => { + const query: TypedDocumentNode<{ greeting: string }> = gql``; + + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + loadQuery({}); + loadQuery({ foo: "bar" }); + loadQuery({ bar: "baz" }); + }); + + it("variables are optional when TVariables are empty", () => { + const query: TypedDocumentNode< + { greeting: string }, + Record + > = gql``; + + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + loadQuery({}); + // @ts-expect-error unknown variable + loadQuery({ foo: "bar" }); + }); + + it("does not allow variables when TVariables is `never`", () => { + const query: TypedDocumentNode<{ greeting: string }, never> = gql``; + + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + // @ts-expect-error no variables argument allowed + loadQuery({}); + // @ts-expect-error no variables argument allowed + loadQuery({ foo: "bar" }); + }); + + it("optional variables are optional to loadQuery", () => { + const query: TypedDocumentNode<{ posts: string[] }, { limit?: number }> = + gql``; + + const [loadQuery] = useLoadableQuery(query); + + loadQuery(); + loadQuery({}); + loadQuery({ limit: 10 }); + loadQuery({ + // @ts-expect-error unknown variable + foo: "bar", + }); + loadQuery({ + limit: 10, + // @ts-expect-error unknown variable + foo: "bar", + }); + }); + + it("enforces required variables when TVariables includes required variables", () => { + const query: TypedDocumentNode<{ character: string }, { id: string }> = + gql``; + + const [loadQuery] = useLoadableQuery(query); + + // @ts-expect-error missing variables argument + loadQuery(); + // @ts-expect-error empty variables + loadQuery({}); + loadQuery({ id: "1" }); + loadQuery({ + // @ts-expect-error unknown variable + foo: "bar", + }); + loadQuery({ + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }); + }); + + it("requires variables with mixed TVariables", () => { + const query: TypedDocumentNode< + { character: string }, + { id: string; language?: string } + > = gql``; + + const [loadQuery] = useLoadableQuery(query); + + // @ts-expect-error missing variables argument + loadQuery(); + // @ts-expect-error empty variables + loadQuery({}); + loadQuery({ id: "1" }); + // @ts-expect-error missing required variable + loadQuery({ language: "en" }); + loadQuery({ id: "1", language: "en" }); + loadQuery({ + // @ts-expect-error unknown variable + foo: "bar", + }); + loadQuery({ + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }); + loadQuery({ + id: "1", + language: "en", + // @ts-expect-error unknown variable + foo: "bar", + }); + }); + + it("returns TData in default case", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it('returns TData | undefined with errorPolicy: "ignore"', () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + errorPolicy: "ignore", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { errorPolicy: "ignore" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it('returns TData | undefined with errorPolicy: "all"', () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + errorPolicy: "all", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { errorPolicy: "all" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it('returns TData with errorPolicy: "none"', () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + errorPolicy: "none", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { errorPolicy: "none" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it("returns DeepPartial with returnPartialData: true", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + returnPartialData: true, + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { returnPartialData: true }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + }); + + it("returns TData with returnPartialData: false", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + returnPartialData: false, + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { returnPartialData: false }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it("returns TData when passing an option that does not affect TData", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + fetchPolicy: "no-cache", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { fetchPolicy: "no-cache" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf(); + } + }); + + it("handles combinations of options", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + returnPartialData: true, + errorPolicy: "ignore", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf< + DeepPartial | undefined + >(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { returnPartialData: true, errorPolicy: "ignore" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf< + DeepPartial | undefined + >(); + } + + { + const [, queryRef] = useLoadableQuery(query, { + returnPartialData: true, + errorPolicy: "none", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { returnPartialData: true, errorPolicy: "none" }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + }); + + it("returns correct TData type when combined options that do not affect TData", () => { + const { query } = useVariablesQueryCase(); + + { + const [, queryRef] = useLoadableQuery(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + errorPolicy: "none", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + + { + const [, queryRef] = useLoadableQuery< + VariablesCaseData, + VariablesCaseVariables + >(query, { + fetchPolicy: "no-cache", + returnPartialData: true, + errorPolicy: "none", + }); + + invariant(queryRef); + + const { data } = useReadQuery(queryRef); + + expectTypeOf(data).toEqualTypeOf>(); + } + }); +}); diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx index 3ba88663d85..452b1ad77de 100644 --- a/src/react/hooks/__tests__/useMutation.test.tsx +++ b/src/react/hooks/__tests__/useMutation.test.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { GraphQLError } from "graphql"; import gql from "graphql-tag"; -import { act } from "react-dom/test-utils"; +import { act } from "@testing-library/react"; import { render, waitFor, screen, renderHook } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import fetchMock from "fetch-mock"; @@ -23,13 +23,14 @@ import { MockSubscriptionLink, mockSingleLink, subscribeAndCount, + MockedResponse, } from "../../../testing"; import { ApolloProvider } from "../../context"; import { useQuery } from "../useQuery"; import { useMutation } from "../useMutation"; import { BatchHttpLink } from "../../../link/batch-http"; import { FetchResult } from "../../../link/core"; -import { spyOnConsole } from "../../../testing/internal"; +import { profileHook, spyOnConsole } from "../../../testing/internal"; describe("useMutation Hook", () => { interface Todo { @@ -719,6 +720,90 @@ describe("useMutation Hook", () => { { interval: 1 } ); }); + + it("resetting while a mutation is running: ensure that the result doesn't end up in the hook", async () => { + const CREATE_TODO_DATA = { + createTodo: { + id: 1, + priority: "Low", + description: "Get milk!", + __typename: "Todo", + }, + }; + + const mocks: MockedResponse[] = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables: { + priority: "Low", + description: "Get milk.", + }, + }, + result: { + data: CREATE_TODO_DATA, + }, + delay: 20, + }, + ]; + + const ProfiledHook = profileHook(() => + useMutation< + { createTodo: Todo }, + { priority: string; description: string } + >(CREATE_TODO_MUTATION) + ); + + render(, { + wrapper: ({ children }) => ( + {children} + ), + }); + + let createTodo: Awaited>[0]; + let reset: Awaited< + ReturnType + >[1]["reset"]; + + { + const [mutate, result] = await ProfiledHook.takeSnapshot(); + createTodo = mutate; + reset = result.reset; + //initial value + expect(result.data).toBe(undefined); + expect(result.loading).toBe(false); + expect(result.called).toBe(false); + } + + let fetchResult: any; + act(() => { + fetchResult = createTodo({ + variables: { priority: "Low", description: "Get milk." }, + }); + }); + + { + const [, result] = await ProfiledHook.takeSnapshot(); + // started loading + expect(result.data).toBe(undefined); + expect(result.loading).toBe(true); + expect(result.called).toBe(true); + } + + act(() => reset()); + + { + const [, result] = await ProfiledHook.takeSnapshot(); + // reset to initial value + expect(result.data).toBe(undefined); + expect(result.loading).toBe(false); + expect(result.called).toBe(false); + } + + expect(await fetchResult).toEqual({ data: CREATE_TODO_DATA }); + + await expect(ProfiledHook).not.toRerender(); + }); }); describe("Callbacks", () => { @@ -2204,8 +2289,9 @@ describe("useMutation Hook", () => { const oldData = cache.readQuery({ query: NumbersQuery }); cache.writeQuery({ query: NumbersQuery, - data: oldData - ? { + data: + oldData ? + { ...oldData, numbers: { ...oldData.numbers, diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index d900a2d53fe..f900f61bbda 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -1,7 +1,7 @@ -import React, { Fragment, ReactNode, useEffect, useState } from "react"; +import React, { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { DocumentNode, GraphQLError } from "graphql"; import gql from "graphql-tag"; -import { act } from "react-dom/test-utils"; +import { act } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { render, screen, waitFor, renderHook } from "@testing-library/react"; import { @@ -22,11 +22,20 @@ import { MockSubscriptionLink, mockSingleLink, tick, + wait, } from "../../../testing"; import { QueryResult } from "../../types/types"; import { useQuery } from "../useQuery"; import { useMutation } from "../useMutation"; -import { profileHook, spyOnConsole } from "../../../testing/internal"; +import { + createProfiler, + profileHook, + spyOnConsole, +} from "../../../testing/internal"; +import { useApolloClient } from "../useApolloClient"; +import { useLazyQuery } from "../useLazyQuery"; + +const IS_REACT_19 = React.version.startsWith("19"); describe("useQuery Hook", () => { describe("General use", () => { @@ -1550,7 +1559,33 @@ describe("useQuery Hook", () => { function checkObservableQueries(expectedLinkCount: number) { const obsQueries = client.getObservableQueries("all"); - expect(obsQueries.size).toBe(2); + /* +This is due to a timing change in React 19 + +In React 18, you observe this pattern: + + 1. render + 2. useState initializer + 3. component continues to render with first state + 4. strictMode: render again + 5. strictMode: call useState initializer again + 6. component continues to render with second state + +now, in React 19 it looks like this: + + 1. render + 2. useState initializer + 3. strictMode: call useState initializer again + 4. component continues to render with one of these two states + 5. strictMode: render again + 6. component continues to render with the same state as during the first render + +Since useQuery breaks the rules of React and mutably creates an ObservableQuery on the state during render if none is present, React 18 did create two, while React 19 only creates one. + +This is pure coincidence though, and the useQuery rewrite that doesn't break the rules of hooks as much and creates the ObservableQuery as part of the state initializer will end up with behaviour closer to the old React 18 behaviour again. + +*/ + expect(obsQueries.size).toBe(IS_REACT_19 ? 1 : 2); const activeSet = new Set(); const inactiveSet = new Set(); @@ -1571,7 +1606,7 @@ describe("useQuery Hook", () => { } }); expect(activeSet.size).toBe(1); - expect(inactiveSet.size).toBe(1); + expect(inactiveSet.size).toBe(obsQueries.size - activeSet.size); } checkObservableQueries(1); @@ -1881,6 +1916,86 @@ describe("useQuery Hook", () => { requestSpy.mockRestore(); }); + // https://github.com/apollographql/apollo-client/issues/9431 + // https://github.com/apollographql/apollo-client/issues/11750 + it("stops polling when component unmounts with cache-and-network fetch policy", async () => { + const query: TypedDocumentNode<{ hello: string }> = gql` + query { + hello + } + `; + + const mocks = [ + { + request: { query }, + result: { data: { hello: "world 1" } }, + }, + { + request: { query }, + result: { data: { hello: "world 2" } }, + }, + { + request: { query }, + result: { data: { hello: "world 3" } }, + }, + ]; + + const cache = new InMemoryCache(); + + const link = new MockLink(mocks); + const requestSpy = jest.spyOn(link, "request"); + const onErrorFn = jest.fn(); + link.setOnError(onErrorFn); + + const ProfiledHook = profileHook(() => + useQuery(query, { pollInterval: 10, fetchPolicy: "cache-and-network" }) + ); + + const client = new ApolloClient({ + queryDeduplication: false, + link, + cache, + }); + + const { unmount } = render(, { + wrapper: ({ children }: any) => ( + {children} + ), + }); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(true); + expect(snapshot.data).toBeUndefined(); + } + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(false); + expect(snapshot.data).toEqual({ hello: "world 1" }); + expect(requestSpy).toHaveBeenCalledTimes(1); + } + + await wait(10); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(false); + expect(snapshot.data).toEqual({ hello: "world 2" }); + expect(requestSpy).toHaveBeenCalledTimes(2); + } + + unmount(); + + await expect(ProfiledHook).not.toRerender({ timeout: 50 }); + + expect(requestSpy).toHaveBeenCalledTimes(2); + expect(onErrorFn).toHaveBeenCalledTimes(0); + }); + it("should stop polling when component is unmounted in Strict Mode", async () => { const query = gql` { @@ -1954,6 +2069,84 @@ describe("useQuery Hook", () => { requestSpy.mockRestore(); }); + // https://github.com/apollographql/apollo-client/issues/9431 + // https://github.com/apollographql/apollo-client/issues/11750 + it("stops polling when component unmounts in strict mode with cache-and-network fetch policy", async () => { + const query: TypedDocumentNode<{ hello: string }> = gql` + query { + hello + } + `; + + const mocks = [ + { + request: { query }, + result: { data: { hello: "world 1" } }, + }, + { + request: { query }, + result: { data: { hello: "world 2" } }, + }, + { + request: { query }, + result: { data: { hello: "world 3" } }, + }, + ]; + + const cache = new InMemoryCache(); + + const link = new MockLink(mocks); + const requestSpy = jest.spyOn(link, "request"); + const onErrorFn = jest.fn(); + link.setOnError(onErrorFn); + + const ProfiledHook = profileHook(() => + useQuery(query, { pollInterval: 10, fetchPolicy: "cache-and-network" }) + ); + + const client = new ApolloClient({ link, cache }); + + const { unmount } = render(, { + wrapper: ({ children }: any) => ( + + {children} + + ), + }); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(true); + expect(snapshot.data).toBeUndefined(); + } + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(false); + expect(snapshot.data).toEqual({ hello: "world 1" }); + expect(requestSpy).toHaveBeenCalledTimes(1); + } + + await wait(10); + + { + const snapshot = await ProfiledHook.takeSnapshot(); + + expect(snapshot.loading).toBe(false); + expect(snapshot.data).toEqual({ hello: "world 2" }); + expect(requestSpy).toHaveBeenCalledTimes(2); + } + + unmount(); + + await expect(ProfiledHook).not.toRerender({ timeout: 50 }); + + expect(requestSpy).toHaveBeenCalledTimes(2); + expect(onErrorFn).toHaveBeenCalledTimes(0); + }); + it("should start and stop polling in Strict Mode", async () => { const query = gql` { @@ -2092,106 +2285,300 @@ describe("useQuery Hook", () => { unmount(); result.current.stopPolling(); }); - }); - describe("Error handling", () => { - it("should pass along GraphQL errors", async () => { - const query = gql` - query TestQuery { - rates(currency: "USD") { - rate + describe("should prevent fetches when `skipPollAttempt` returns `false`", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + it("when defined as a global default option", async () => { + const skipPollAttempt = jest.fn().mockImplementation(() => false); + + const query = gql` + { + hello } - } - `; + `; + const link = mockSingleLink( + { + request: { query }, + result: { data: { hello: "world 1" } }, + }, + { + request: { query }, + result: { data: { hello: "world 2" } }, + }, + { + request: { query }, + result: { data: { hello: "world 3" } }, + } + ); - const mocks = [ - { - request: { query }, - result: { - errors: [new GraphQLError("error")], + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + defaultOptions: { + watchQuery: { + skipPollAttempt, + }, }, - }, - ]; + }); - const cache = new InMemoryCache(); - const wrapper = ({ children }: any) => ( - - {children} - - ); + const wrapper = ({ children }: any) => ( + {children} + ); - const { result } = renderHook(() => useQuery(query), { wrapper }); + const { result } = renderHook( + () => useQuery(query, { pollInterval: 10 }), + { wrapper } + ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("error"); - }); + expect(result.current.loading).toBe(true); + expect(result.current.data).toBe(undefined); - it("calls `onError` when a GraphQL error is returned", async () => { - const query = gql` - { - hello - } - `; - const mocks = [ - { - request: { query }, - result: { - errors: [new GraphQLError("error")], + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 1" }); }, - }, - ]; + { interval: 1 } + ); - const cache = new InMemoryCache(); - const wrapper = ({ children }: any) => ( - - {children} - - ); + expect(result.current.loading).toBe(false); - const onError = jest.fn(); - const { result } = renderHook(() => useQuery(query, { onError }), { - wrapper, - }); + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 2" }); + }, + { interval: 1 } + ); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); + skipPollAttempt.mockImplementation(() => true); + expect(result.current.loading).toBe(false); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } - ); + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } + ); - expect(result.current.data).toBeUndefined(); - expect(result.current.error).toBeInstanceOf(ApolloError); - expect(result.current.error!.message).toBe("error"); + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } + ); - await waitFor(() => { - expect(onError).toHaveBeenCalledTimes(1); - }); - await waitFor(() => { - expect(onError).toHaveBeenCalledWith( - new ApolloError({ graphQLErrors: [new GraphQLError("error")] }) + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } ); - }); - }); - it("calls `onError` when a network error has occurred", async () => { - const query = gql` - { - hello - } - `; - const mocks = [ + skipPollAttempt.mockImplementation(() => false); + expect(result.current.loading).toBe(false); + + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 3" }); + }, + { interval: 1 } + ); + }); + + it("when defined for a single query", async () => { + const skipPollAttempt = jest.fn().mockImplementation(() => false); + + const query = gql` + { + hello + } + `; + const mocks = [ + { + request: { query }, + result: { data: { hello: "world 1" } }, + }, + { + request: { query }, + result: { data: { hello: "world 2" } }, + }, + { + request: { query }, + result: { data: { hello: "world 3" } }, + }, + ]; + + const cache = new InMemoryCache(); + const wrapper = ({ children }: any) => ( + + {children} + + ); + + const { result } = renderHook( + () => + useQuery(query, { + pollInterval: 10, + skipPollAttempt, + }), + { wrapper } + ); + + expect(result.current.loading).toBe(true); + expect(result.current.data).toBe(undefined); + + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 1" }); + }, + { interval: 1 } + ); + + expect(result.current.loading).toBe(false); + + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 2" }); + }, + { interval: 1 } + ); + + skipPollAttempt.mockImplementation(() => true); + expect(result.current.loading).toBe(false); + + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } + ); + + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } + ); + + await jest.advanceTimersByTime(12); + await waitFor( + () => expect(result.current.data).toEqual({ hello: "world 2" }), + { interval: 1 } + ); + + skipPollAttempt.mockImplementation(() => false); + expect(result.current.loading).toBe(false); + + await waitFor( + () => { + expect(result.current.data).toEqual({ hello: "world 3" }); + }, + { interval: 1 } + ); + }); + }); + }); + + describe("Error handling", () => { + it("should pass along GraphQL errors", async () => { + const query = gql` + query TestQuery { + rates(currency: "USD") { + rate + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError("error")], + }, + }, + ]; + + const cache = new InMemoryCache(); + const wrapper = ({ children }: any) => ( + + {children} + + ); + + const { result } = renderHook(() => useQuery(query), { wrapper }); + + expect(result.current.loading).toBe(true); + expect(result.current.data).toBe(undefined); + await waitFor( + () => { + expect(result.current.loading).toBe(false); + }, + { interval: 1 } + ); + expect(result.current.error).toBeInstanceOf(ApolloError); + expect(result.current.error!.message).toBe("error"); + }); + + it("calls `onError` when a GraphQL error is returned", async () => { + const query = gql` + { + hello + } + `; + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError("error")], + }, + }, + ]; + + const cache = new InMemoryCache(); + const wrapper = ({ children }: any) => ( + + {children} + + ); + + const onError = jest.fn(); + const { result } = renderHook(() => useQuery(query, { onError }), { + wrapper, + }); + + expect(result.current.loading).toBe(true); + expect(result.current.data).toBe(undefined); + + await waitFor( + () => { + expect(result.current.loading).toBe(false); + }, + { interval: 1 } + ); + + expect(result.current.data).toBeUndefined(); + expect(result.current.error).toBeInstanceOf(ApolloError); + expect(result.current.error!.message).toBe("error"); + + await waitFor(() => { + expect(onError).toHaveBeenCalledTimes(1); + }); + await waitFor(() => { + expect(onError).toHaveBeenCalledWith( + new ApolloError({ graphQLErrors: [new GraphQLError("error")] }) + ); + }); + }); + + it("calls `onError` when a network error has occurred", async () => { + const query = gql` + { + hello + } + `; + const mocks = [ { request: { query }, error: new Error("Could not fetch"), @@ -3823,66 +4210,1051 @@ describe("useQuery Hook", () => { }); }); - describe("Refetching", () => { - it("refetching with different variables", async () => { - const query = gql` - query ($id: Int) { - hello(id: $id) + // https://github.com/apollographql/apollo-client/issues/11400 + it("does not return partial data unexpectedly when one query errors, then another succeeds with overlapping data", async () => { + interface Query1 { + person: { + __typename: "Person"; + id: number; + firstName: string; + alwaysFails: boolean; + } | null; + } + + interface Query2 { + person: { __typename: "Person"; id: number; lastName: string } | null; + } + + interface Variables { + id: number; + } + + const user = userEvent.setup(); + + const query1: TypedDocumentNode = gql` + query PersonQuery1($id: ID!) { + person(id: $id) { + id + firstName + alwaysFails } - `; + } + `; - const mocks = [ + const query2: TypedDocumentNode = gql` + query PersonQuery2($id: ID!) { + person(id: $id) { + id + lastName + } + } + `; + + const Profiler = createProfiler({ + initialSnapshot: { + useQueryResult: null as QueryResult | null, + useLazyQueryResult: null as QueryResult | null, + }, + }); + + const client = new ApolloClient({ + link: new MockLink([ { - request: { query, variables: { id: 1 } }, - result: { data: { hello: "world 1" } }, + request: { query: query1, variables: { id: 1 } }, + result: { + data: { person: null }, + errors: [new GraphQLError("Intentional error")], + }, + maxUsageCount: Number.POSITIVE_INFINITY, + delay: 20, }, { - request: { query, variables: { id: 2 } }, - result: { data: { hello: "world 2" } }, - delay: 10, + request: { query: query2, variables: { id: 1 } }, + result: { + data: { person: { __typename: "Person", id: 1, lastName: "Doe" } }, + }, + delay: 20, }, - ]; + ]), + cache: new InMemoryCache(), + }); - const cache = new InMemoryCache(); - const wrapper = ({ children }: any) => ( - - {children} - - ); + function App() { + const useQueryResult = useQuery(query1, { + variables: { id: 1 }, + // This is necessary to reproduce the behavior + notifyOnNetworkStatusChange: true, + }); - const { result } = renderHook( - () => - useQuery(query, { - variables: { id: 1 }, - notifyOnNetworkStatusChange: true, - }), - { wrapper } - ); + const [execute, useLazyQueryResult] = useLazyQuery(query2, { + variables: { id: 1 }, + }); - expect(result.current.loading).toBe(true); - expect(result.current.data).toBe(undefined); + Profiler.replaceSnapshot({ useQueryResult, useLazyQueryResult }); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, - { interval: 1 } + return ( + <> + + + ); - expect(result.current.data).toEqual({ hello: "world 1" }); + } - result.current.refetch({ id: 2 }); - await waitFor( - () => { - expect(result.current.loading).toBe(true); - }, - { interval: 1 } - ); - expect(result.current.data).toBe(undefined); + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); - await waitFor( - () => { - expect(result.current.loading).toBe(false); - }, + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Run 2nd query"))); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + // ensure we aren't setting a value on the observable query that contains + // the partial result + expect( + snapshot.useQueryResult?.observable.getCurrentResult(false) + ).toEqual({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + errors: [new GraphQLError("Intentional error")], + loading: false, + networkStatus: NetworkStatus.error, + partial: true, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { person: { __typename: "Person", id: 1, lastName: "Doe" } }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Reload 1st query"))); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { person: { __typename: "Person", id: 1, lastName: "Doe" } }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: false, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + networkStatus: NetworkStatus.error, + }); + + // ensure we aren't setting a value on the observable query that contains + // the partial result + expect( + snapshot.useQueryResult?.observable.getCurrentResult(false) + ).toEqual({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + errors: [new GraphQLError("Intentional error")], + loading: false, + networkStatus: NetworkStatus.error, + partial: true, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { person: { __typename: "Person", id: 1, lastName: "Doe" } }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + it("rerenders errored query for full cache write", async () => { + interface Query1 { + person: { + __typename: "Person"; + id: number; + firstName: string; + } | null; + } + + interface Query2 { + person: { + __typename: "Person"; + id: number; + firstName: string; + lastName: string; + } | null; + } + + interface Variables { + id: number; + } + + const user = userEvent.setup(); + + const query1: TypedDocumentNode = gql` + query PersonQuery1($id: ID!) { + person(id: $id) { + id + firstName + } + } + `; + + const query2: TypedDocumentNode = gql` + query PersonQuery2($id: ID!) { + person(id: $id) { + id + firstName + lastName + } + } + `; + + const Profiler = createProfiler({ + initialSnapshot: { + useQueryResult: null as QueryResult | null, + useLazyQueryResult: null as QueryResult | null, + }, + }); + + const client = new ApolloClient({ + link: new MockLink([ + { + request: { query: query1, variables: { id: 1 } }, + result: { + data: { person: null }, + errors: [new GraphQLError("Intentional error")], + }, + delay: 20, + }, + { + request: { query: query2, variables: { id: 1 } }, + result: { + data: { + person: { + __typename: "Person", + id: 1, + firstName: "John", + lastName: "Doe", + }, + }, + }, + delay: 20, + }, + ]), + cache: new InMemoryCache(), + }); + + function App() { + const useQueryResult = useQuery(query1, { + variables: { id: 1 }, + // This is necessary to reproduce the behavior + notifyOnNetworkStatusChange: true, + }); + + const [execute, useLazyQueryResult] = useLazyQuery(query2, { + variables: { id: 1 }, + }); + + Profiler.replaceSnapshot({ useQueryResult, useLazyQueryResult }); + + return ; + } + + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Run 2nd query"))); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + // We don't see the update from the cache for one more render cycle, hence + // why this is still showing the error result even though the result from + // the other query has finished and re-rendered. + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { + person: { + __typename: "Person", + id: 1, + firstName: "John", + lastName: "Doe", + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: { + person: { + __typename: "Person", + id: 1, + firstName: "John", + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { + person: { + __typename: "Person", + id: 1, + firstName: "John", + lastName: "Doe", + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + it("does not rerender or refetch queries with errors for partial cache writes with returnPartialData: true", async () => { + interface Query1 { + person: { + __typename: "Person"; + id: number; + firstName: string; + alwaysFails: boolean; + } | null; + } + + interface Query2 { + person: { + __typename: "Person"; + id: number; + lastName: string; + } | null; + } + + interface Variables { + id: number; + } + + const user = userEvent.setup(); + + const query1: TypedDocumentNode = gql` + query PersonQuery1($id: ID!) { + person(id: $id) { + id + firstName + alwaysFails + } + } + `; + + const query2: TypedDocumentNode = gql` + query PersonQuery2($id: ID!) { + person(id: $id) { + id + lastName + } + } + `; + + const Profiler = createProfiler({ + initialSnapshot: { + useQueryResult: null as QueryResult | null, + useLazyQueryResult: null as QueryResult | null, + }, + }); + + const client = new ApolloClient({ + link: new MockLink([ + { + request: { query: query1, variables: { id: 1 } }, + result: { + data: { person: null }, + errors: [new GraphQLError("Intentional error")], + }, + delay: 20, + maxUsageCount: Number.POSITIVE_INFINITY, + }, + { + request: { query: query2, variables: { id: 1 } }, + result: { + data: { + person: { + __typename: "Person", + id: 1, + lastName: "Doe", + }, + }, + }, + delay: 20, + }, + ]), + cache: new InMemoryCache(), + }); + + function App() { + const useQueryResult = useQuery(query1, { + variables: { id: 1 }, + notifyOnNetworkStatusChange: true, + returnPartialData: true, + }); + + const [execute, useLazyQueryResult] = useLazyQuery(query2, { + variables: { id: 1 }, + }); + + Profiler.replaceSnapshot({ useQueryResult, useLazyQueryResult }); + + return ; + } + + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: false, + data: undefined, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Run 2nd query"))); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + error: new ApolloError({ + graphQLErrors: [new GraphQLError("Intentional error")], + }), + loading: false, + networkStatus: NetworkStatus.error, + }); + + expect(snapshot.useLazyQueryResult).toMatchObject({ + called: true, + data: { + person: { + __typename: "Person", + id: 1, + lastName: "Doe", + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + it("delivers the full network response when a merge function returns an incomplete result", async () => { + const query = gql` + query { + author { + id + name + post { + id + title + } + } + } + `; + + const Profiler = createProfiler({ + initialSnapshot: { + useQueryResult: null as QueryResult | null, + }, + }); + + const client = new ApolloClient({ + link: new MockLink([ + { + request: { query }, + result: { + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee", + post: { + __typename: "Post", + id: 1, + title: "Title", + }, + }, + }, + }, + delay: 20, + }, + ]), + cache: new InMemoryCache({ + typePolicies: { + Author: { + fields: { + post: { + merge: () => { + return {}; + }, + }, + }, + }, + }, + }), + }); + + function App() { + const useQueryResult = useQuery(query); + + Profiler.replaceSnapshot({ useQueryResult }); + + return null; + } + + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee", + post: { + __typename: "Post", + title: "Title", + }, + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + it("triggers a network request and rerenders with the new result when a mutation causes a partial cache update due to an incomplete merge function result", async () => { + const query = gql` + query { + author { + id + name + post { + id + title + } + } + } + `; + const mutation = gql` + mutation { + updateAuthor { + author { + id + name + post { + id + title + } + } + } + } + `; + + const user = userEvent.setup(); + + const Profiler = createProfiler({ + initialSnapshot: { + useQueryResult: null as QueryResult | null, + }, + }); + + const client = new ApolloClient({ + link: new MockLink([ + { + request: { query }, + result: { + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee", + post: { + __typename: "Post", + id: 1, + title: "Title", + }, + }, + }, + }, + delay: 20, + }, + { + request: { query }, + result: { + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee (refetch)", + post: { + __typename: "Post", + id: 1, + title: "Title", + }, + }, + }, + }, + delay: 20, + }, + { + request: { query: mutation }, + result: { + data: { + updateAuthor: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee (mutation)", + post: { + __typename: "Post", + id: 1, + title: "Title", + }, + }, + }, + }, + }, + delay: 20, + }, + ]), + cache: new InMemoryCache({ + typePolicies: { + Author: { + fields: { + post: { + // this is necessary to reproduce the issue + merge: () => { + return {}; + }, + }, + }, + }, + }, + }), + }); + + function App() { + const useQueryResult = useQuery(query); + const [mutate] = useMutation(mutation); + + Profiler.replaceSnapshot({ useQueryResult }); + + return ; + } + + render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: undefined, + loading: true, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee", + post: { + __typename: "Post", + title: "Title", + }, + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Run mutation"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: { + author: { + __typename: "Author", + id: 1, + name: "Author Lee", + post: { + __typename: "Post", + title: "Title", + }, + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.useQueryResult).toMatchObject({ + data: { + author: { + __typename: "Author", + id: 1, + // Because of the merge function returning an incomplete result, we + // don't expect to see the value returned from the mutation. The + // partial result from the mutation causes a network fetch which + // renders the refetched result. + name: "Author Lee (refetch)", + post: { + __typename: "Post", + title: "Title", + }, + }, + }, + loading: false, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + describe("Refetching", () => { + it("refetching with different variables", async () => { + const query = gql` + query ($id: Int) { + hello(id: $id) + } + `; + + const mocks = [ + { + request: { query, variables: { id: 1 } }, + result: { data: { hello: "world 1" } }, + }, + { + request: { query, variables: { id: 2 } }, + result: { data: { hello: "world 2" } }, + delay: 10, + }, + ]; + + const cache = new InMemoryCache(); + const wrapper = ({ children }: any) => ( + + {children} + + ); + + const { result } = renderHook( + () => + useQuery(query, { + variables: { id: 1 }, + notifyOnNetworkStatusChange: true, + }), + { wrapper } + ); + + expect(result.current.loading).toBe(true); + expect(result.current.data).toBe(undefined); + + await waitFor( + () => { + expect(result.current.loading).toBe(false); + }, + { interval: 1 } + ); + expect(result.current.data).toEqual({ hello: "world 1" }); + + result.current.refetch({ id: 2 }); + await waitFor( + () => { + expect(result.current.loading).toBe(true); + }, + { interval: 1 } + ); + expect(result.current.data).toBe(undefined); + + await waitFor( + () => { + expect(result.current.loading).toBe(false); + }, { interval: 1 } ); expect(result.current.data).toEqual({ hello: "world 2" }); @@ -4007,7 +5379,7 @@ describe("useQuery Hook", () => { primes: [13, 17, 19, 23, 29], }, }, - delay: 10, + delay: 25, }, ]; @@ -4300,6 +5672,138 @@ describe("useQuery Hook", () => { }); }); }); + + it("keeps cache consistency when a call to refetchQueries is interrupted with another query caused by changing variables and the second query returns before the first one", async () => { + const CAR_QUERY_BY_ID = gql` + query Car($id: Int) { + car(id: $id) { + make + model + } + } + `; + + const mocks = { + 1: [ + { + car: { + make: "Audi", + model: "A4", + __typename: "Car", + }, + }, + { + car: { + make: "Audi", + model: "A3", // Changed + __typename: "Car", + }, + }, + ], + 2: [ + { + car: { + make: "Audi", + model: "RS8", + __typename: "Car", + }, + }, + ], + }; + + const link = new ApolloLink( + (operation) => + new Observable((observer) => { + if (operation.variables.id === 1) { + // Queries for this ID return after a delay + setTimeout(() => { + const data = mocks[1].splice(0, 1).pop(); + observer.next({ data }); + observer.complete(); + }, 100); + } else if (operation.variables.id === 2) { + // Queries for this ID return immediately + const data = mocks[2].splice(0, 1).pop(); + observer.next({ data }); + observer.complete(); + } else { + observer.error(new Error("Unexpected query")); + } + }) + ); + + const hookResponse = jest.fn().mockReturnValue(null); + + function Component({ children, id }: any) { + const result = useQuery(CAR_QUERY_BY_ID, { + variables: { id }, + notifyOnNetworkStatusChange: true, + fetchPolicy: "network-only", + }); + const client = useApolloClient(); + const hasRefetchedRef = useRef(false); + + useEffect(() => { + if ( + result.networkStatus === NetworkStatus.ready && + !hasRefetchedRef.current + ) { + client.reFetchObservableQueries(); + hasRefetchedRef.current = true; + } + }, [result.networkStatus]); + + return children(result); + } + + const { rerender } = render( + {hookResponse}, + { + wrapper: ({ children }) => ( + {children} + ), + } + ); + + await waitFor(() => { + // Resolves as soon as reFetchObservableQueries is + // called, but before the result is returned + expect(hookResponse).toHaveBeenCalledTimes(3); + }); + + rerender({hookResponse}); + + await waitFor(() => { + // All results are returned + expect(hookResponse).toHaveBeenCalledTimes(5); + }); + + expect(hookResponse.mock.calls.map((call) => call[0].data)).toEqual([ + undefined, + { + car: { + __typename: "Car", + make: "Audi", + model: "A4", + }, + }, + { + car: { + __typename: "Car", + make: "Audi", + model: "A4", + }, + }, + undefined, + { + car: { + __typename: "Car", + make: "Audi", + model: "RS8", + }, + }, + ]); + }); }); describe("Callbacks", () => { @@ -6409,11 +7913,10 @@ describe("useQuery Hook", () => { observer.next({ data: { people: - gender === "all" - ? peopleData - : gender - ? peopleData.filter((person) => person.gender === gender) - : peopleData, + gender === "all" ? peopleData + : gender ? + peopleData.filter((person) => person.gender === gender) + : peopleData, }, }); observer.complete(); diff --git a/src/react/hooks/__tests__/useQueryRefHandlers.test.tsx b/src/react/hooks/__tests__/useQueryRefHandlers.test.tsx new file mode 100644 index 00000000000..012f7fb1872 --- /dev/null +++ b/src/react/hooks/__tests__/useQueryRefHandlers.test.tsx @@ -0,0 +1,1929 @@ +import React from "react"; +import { act, render, screen } from "@testing-library/react"; +import { + ApolloClient, + InMemoryCache, + NetworkStatus, + TypedDocumentNode, + gql, +} from "../../../core"; +import { MockLink, MockedResponse } from "../../../testing"; +import { + PaginatedCaseData, + SimpleCaseData, + createProfiler, + renderWithClient, + setupPaginatedCase, + setupSimpleCase, + useTrackRenders, +} from "../../../testing/internal"; +import { useQueryRefHandlers } from "../useQueryRefHandlers"; +import { UseReadQueryResult, useReadQuery } from "../useReadQuery"; +import { Suspense } from "react"; +import { createQueryPreloader } from "../../query-preloader/createQueryPreloader"; +import userEvent from "@testing-library/user-event"; +import { QueryRef } from "../../internal"; +import { useBackgroundQuery } from "../useBackgroundQuery"; +import { useLoadableQuery } from "../useLoadableQuery"; +import { concatPagination } from "../../../utilities"; + +test("does not interfere with updates from useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + // We can ignore the return result here since we are testing the mechanics + // of this hook to ensure it doesn't interfere with the updates from + // useReadQuery + useQueryRefHandlers(queryRef); + + return ( + }> + + + ); + } + + const { rerender } = renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ query, data: { greeting: "Hello again" } }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + rerender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("refetches and resuspends when calling refetch", async () => { + const { query, mocks: defaultMocks } = setupSimpleCase(); + + const user = userEvent.setup(); + + const mocks = [ + defaultMocks[0], + { + request: { query }, + result: { data: { greeting: "Hello again" } }, + delay: 20, + }, + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { refetch } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test('honors refetchWritePolicy set to "merge"', async () => { + const query: TypedDocumentNode< + { primes: number[] }, + { min: number; max: number } + > = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const user = userEvent.setup(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + refetchWritePolicy: "merge", + variables: { min: 0, max: 12 }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { refetch } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [ + [2, 3, 5, 7, 11], + [13, 17, 19, 23, 29], + ], + ]); + } + + await expect(Profiler).not.toRerender(); +}); + +test('honors refetchWritePolicy set to "overwrite"', async () => { + const query: TypedDocumentNode< + { primes: number[] }, + { min: number; max: number } + > = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const user = userEvent.setup(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + refetchWritePolicy: "overwrite", + variables: { min: 0, max: 12 }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { refetch } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [undefined, [13, 17, 19, 23, 29]], + ]); + } + + await expect(Profiler).not.toRerender(); +}); + +test('defaults refetchWritePolicy to "overwrite"', async () => { + const query: TypedDocumentNode< + { primes: number[] }, + { min: number; max: number } + > = gql` + query GetPrimes($min: number, $max: number) { + primes(min: $min, max: $max) + } + `; + + interface QueryData { + primes: number[]; + } + + const mocks = [ + { + request: { query, variables: { min: 0, max: 12 } }, + result: { data: { primes: [2, 3, 5, 7, 11] } }, + delay: 10, + }, + { + request: { query, variables: { min: 12, max: 30 } }, + result: { data: { primes: [13, 17, 19, 23, 29] } }, + delay: 10, + }, + ]; + + const mergeParams: [number[] | undefined, number[]][] = []; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + primes: { + keyArgs: false, + merge(existing: number[] | undefined, incoming: number[]) { + mergeParams.push([existing, incoming]); + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + }, + }); + + const user = userEvent.setup(); + const client = new ApolloClient({ + link: new MockLink(mocks), + cache, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + variables: { min: 0, max: 12 }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { refetch } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [2, 3, 5, 7, 11] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([[undefined, [2, 3, 5, 7, 11]]]); + } + + await act(() => user.click(screen.getByText("Refetch"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { primes: [13, 17, 19, 23, 29] }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(mergeParams).toEqual([ + [undefined, [2, 3, 5, 7, 11]], + [undefined, [13, 17, 19, 23, 29]], + ]); + } + + await expect(Profiler).not.toRerender(); +}); + +test("`refetch` works with startTransition", async () => { + type Variables = { + id: string; + }; + + interface Data { + todo: { + id: string; + name: string; + completed: boolean; + }; + } + const user = userEvent.setup(); + + const query: TypedDocumentNode = gql` + query TodoItemQuery($id: ID!) { + todo(id: $id) { + id + name + completed + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + }, + delay: 10, + }, + { + request: { query, variables: { id: "1" } }, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + }, + delay: 10, + }, + ]; + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache(), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { variables: { id: "1" } }); + + function App() { + useTrackRenders(); + const { refetch } = useQueryRefHandlers(queryRef); + const [isPending, startTransition] = React.useTransition(); + + Profiler.mergeSnapshot({ isPending }); + + return ( + <> + + }> + + + + ); + } + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function Todo() { + useTrackRenders(); + const result = useReadQuery(queryRef); + const { todo } = result.data; + + Profiler.mergeSnapshot({ result }); + + return ( +
+ {todo.name} + {todo.completed && " (completed)"} +
+ ); + } + + render(, { wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + const button = screen.getByText("Refetch"); + await act(() => user.click(button)); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, Todo]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { todo: { id: "1", name: "Clean room", completed: false } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, Todo]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { todo: { id: "1", name: "Clean room", completed: true } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("`refetch` works with startTransition from useBackgroundQuery and usePreloadedQueryHandlers", async () => { + const { query, mocks: defaultMocks } = setupSimpleCase(); + + const user = userEvent.setup(); + + const mocks = [ + defaultMocks[0], + { + request: { query }, + result: { data: { greeting: "Hello again" } }, + delay: 20, + }, + { + request: { query }, + result: { data: { greeting: "You again?" } }, + delay: 20, + }, + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + useBackgroundQueryIsPending: false, + usePreloadedQueryHandlersIsPending: false, + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ queryRef }: { queryRef: QueryRef }) { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const { refetch } = useQueryRefHandlers(queryRef); + + Profiler.mergeSnapshot({ + usePreloadedQueryHandlersIsPending: isPending, + result: useReadQuery(queryRef), + }); + + return ( + + ); + } + + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const [queryRef, { refetch }] = useBackgroundQuery(query); + + Profiler.mergeSnapshot({ useBackgroundQueryIsPending: isPending }); + + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch from parent"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: true, + usePreloadedQueryHandlersIsPending: false, + result: { + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + usePreloadedQueryHandlersIsPending: false, + result: { + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Refetch from child"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + usePreloadedQueryHandlersIsPending: true, + result: { + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + usePreloadedQueryHandlersIsPending: false, + result: { + data: { greeting: "You again?" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("refetches from queryRefs produced by useBackgroundQuery", async () => { + const { query, mocks: defaultMocks } = setupSimpleCase(); + + const user = userEvent.setup(); + + const mocks = [ + defaultMocks[0], + { + request: { query }, + result: { data: { greeting: "Hello again" } }, + delay: 20, + }, + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ queryRef }: { queryRef: QueryRef }) { + const { refetch } = useQueryRefHandlers(queryRef); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return ; + } + + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); + + return ( + <> + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("refetches from queryRefs produced by useLoadableQuery", async () => { + const { query, mocks: defaultMocks } = setupSimpleCase(); + + const user = userEvent.setup(); + + const mocks = [ + defaultMocks[0], + { + request: { query }, + result: { data: { greeting: "Hello again" } }, + delay: 20, + }, + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ queryRef }: { queryRef: QueryRef }) { + const { refetch } = useQueryRefHandlers(queryRef); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return ; + } + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Refetch"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello again" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("resuspends when calling `fetchMore`", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, + }, + }, + }), + link, + }); + const preloadQuery = createQueryPreloader(client); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + + + + ); + } + + const queryRef = preloadQuery(query); + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("properly uses `updateQuery` when calling `fetchMore`", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + + + + ); + } + + const queryRef = preloadQuery(query); + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("properly uses cache field policies when calling `fetchMore` without `updateQuery`", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: concatPagination(), + }, + }, + }, + }), + link, + }); + const preloadQuery = createQueryPreloader(client); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + return ( + <> + + }> + + + + ); + } + + const queryRef = preloadQuery(query); + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("paginates from queryRefs produced by useBackgroundQuery", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, + }, + }, + }), + link, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ + queryRef, + }: { + queryRef: QueryRef; + }) { + useTrackRenders(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return ( + + ); + } + + function App() { + useTrackRenders(); + const [queryRef] = useBackgroundQuery(query); + + return ( + }> + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("paginates from queryRefs produced by useLoadableQuery", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, + }, + }, + }), + link, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ + queryRef, + }: { + queryRef: QueryRef; + }) { + useTrackRenders(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return ( + + ); + } + + function App() { + useTrackRenders(); + const [loadQuery, queryRef] = useLoadableQuery(query); + + return ( + <> + + }> + {queryRef && } + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + // initial render + await Profiler.takeRender(); + + await act(() => user.click(screen.getByText("Load query"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("`fetchMore` works with startTransition", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, + }, + }, + }), + link, + }); + const preloadQuery = createQueryPreloader(client); + + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + Profiler.mergeSnapshot({ isPending }); + + return ( + <> + + }> + + + + ); + } + + const queryRef = preloadQuery(query); + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Load next"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("`fetchMore` works with startTransition from useBackgroundQuery and useQueryRefHandlers", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: { keyArgs: false }, + }, + }, + }, + }), + link, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + useBackgroundQueryIsPending: false, + useQueryRefHandlersIsPending: false, + result: null as UseReadQueryResult | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return

Loading

; + } + + function ReadQueryHook({ + queryRef, + }: { + queryRef: QueryRef; + }) { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const { fetchMore } = useQueryRefHandlers(queryRef); + + Profiler.mergeSnapshot({ + useQueryRefHandlersIsPending: isPending, + result: useReadQuery(queryRef), + }); + + return ( + + ); + } + + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const [queryRef, { fetchMore }] = useBackgroundQuery(query); + + Profiler.mergeSnapshot({ useBackgroundQueryIsPending: isPending }); + + return ( + <> + + }> + + + + ); + } + + renderWithClient(, { client, wrapper: Profiler }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await act(() => user.click(screen.getByText("Paginate from parent"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: true, + useQueryRefHandlersIsPending: false, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + useQueryRefHandlersIsPending: false, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Paginate from child"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + useQueryRefHandlersIsPending: true, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([ReadQueryHook]); + expect(snapshot).toEqual({ + useBackgroundQueryIsPending: false, + useQueryRefHandlersIsPending: false, + result: { + data: { + letters: [ + { __typename: "Letter", letter: "E", position: 5 }, + { __typename: "Letter", letter: "F", position: 6 }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender(); +}); diff --git a/src/react/hooks/__tests__/useReactiveVar.test.tsx b/src/react/hooks/__tests__/useReactiveVar.test.tsx index 3c5e8afdbf3..8979f9bbd32 100644 --- a/src/react/hooks/__tests__/useReactiveVar.test.tsx +++ b/src/react/hooks/__tests__/useReactiveVar.test.tsx @@ -6,6 +6,7 @@ import { makeVar } from "../../../core"; import { useReactiveVar } from "../useReactiveVar"; const IS_REACT_18 = React.version.startsWith("18"); +const IS_REACT_19 = React.version.startsWith("19"); describe("useReactiveVar Hook", () => { it("works with one component", async () => { @@ -277,7 +278,7 @@ describe("useReactiveVar Hook", () => { ); await waitFor(() => { - if (IS_REACT_18) { + if (IS_REACT_18 || IS_REACT_19) { expect(mock).toHaveBeenCalledTimes(3); expect(mock).toHaveBeenNthCalledWith(1, 0); expect(mock).toHaveBeenNthCalledWith(2, 0); diff --git a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx index 468fe8f4fc5..31855bf83a4 100644 --- a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx +++ b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, StrictMode, Suspense } from "react"; +import React, { Fragment, StrictMode, Suspense, useTransition } from "react"; import { act, screen, @@ -51,7 +51,15 @@ import { RefetchWritePolicy, WatchQueryFetchPolicy, } from "../../../core/watchQueryOptions"; -import { profile, spyOnConsole } from "../../../testing/internal"; +import { + PaginatedCaseData, + PaginatedCaseVariables, + createProfiler, + profile, + setupPaginatedCase, + spyOnConsole, + useTrackRenders, +} from "../../../testing/internal"; type RenderSuspenseHookOptions = Omit< RenderHookOptions, @@ -250,17 +258,15 @@ interface VariablesCaseVariables { function useVariablesQueryCase() { const CHARACTERS = ["Spider-Man", "Black Widow", "Iron Man", "Hulk"]; - const query: TypedDocumentNode< - VariablesCaseData, - VariablesCaseVariables - > = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } } - } - `; + `; const mocks = CHARACTERS.map((name, index) => ({ request: { query, variables: { id: String(index + 1) } }, @@ -357,7 +363,7 @@ describe("useSuspenseQuery", () => { const Component = () => { const result = useSuspenseQuery(query); - ProfiledApp.updateSnapshot(result); + ProfiledApp.replaceSnapshot(result); return
{result.data.greeting}
; }; @@ -1666,17 +1672,15 @@ describe("useSuspenseQuery", () => { }); it("uses cached result with network request and does not suspend when switching back to already used variables while using `cache-and-network` fetch policy", async () => { - const query: TypedDocumentNode< - VariablesCaseData, - VariablesCaseVariables - > = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } } - } - `; + `; const mocks = [ { @@ -1783,17 +1787,15 @@ describe("useSuspenseQuery", () => { }); it("refetches and suspends when switching back to already used variables while using `network-only` fetch policy", async () => { - const query: TypedDocumentNode< - VariablesCaseData, - VariablesCaseVariables - > = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } } - } - `; + `; const mocks = [ { @@ -1889,17 +1891,15 @@ describe("useSuspenseQuery", () => { }); it("refetches and suspends when switching back to already used variables while using `no-cache` fetch policy", async () => { - const query: TypedDocumentNode< - VariablesCaseData, - VariablesCaseVariables - > = gql` - query CharacterQuery($id: ID!) { - character(id: $id) { - id - name + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } } - } - `; + `; const mocks = [ { @@ -3650,8 +3650,7 @@ describe("useSuspenseQuery", () => { }); await waitFor(() => expect(renders.errorCount).toBe(1)); - - expect(client.getObservableQueries().size).toBe(0); + await waitFor(() => expect(client.getObservableQueries().size).toBe(0)); }); it('throws network errors when errorPolicy is set to "none"', async () => { @@ -5561,6 +5560,100 @@ describe("useSuspenseQuery", () => { expect(fetchCount).toBe(1); }); + // https://github.com/apollographql/apollo-client/issues/11768 + it("does not make network requests when using `skipToken` with strict mode", async () => { + const { query, mocks } = useVariablesQueryCase(); + + let fetchCount = 0; + + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; + + const mock = mocks.find(({ request }) => + equal(request.variables, operation.variables) + ); + + if (!mock) { + throw new Error("Could not find mock for operation"); + } + + observer.next(mock.result); + observer.complete(); + }); + }); + + const { result, rerender } = renderSuspenseHook( + ({ skip, id }) => + useSuspenseQuery(query, skip ? skipToken : { variables: { id } }), + { mocks, link, strictMode: true, initialProps: { skip: true, id: "1" } } + ); + + expect(fetchCount).toBe(0); + + rerender({ skip: false, id: "1" }); + + expect(fetchCount).toBe(1); + + await waitFor(() => { + expect(result.current).toMatchObject({ + ...mocks[0].result, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + }); + + rerender({ skip: true, id: "2" }); + + expect(fetchCount).toBe(1); + }); + + it("does not make network requests when using `skip` with strict mode", async () => { + const { query, mocks } = useVariablesQueryCase(); + + let fetchCount = 0; + + const link = new ApolloLink((operation) => { + return new Observable((observer) => { + fetchCount++; + + const mock = mocks.find(({ request }) => + equal(request.variables, operation.variables) + ); + + if (!mock) { + throw new Error("Could not find mock for operation"); + } + + observer.next(mock.result); + observer.complete(); + }); + }); + + const { result, rerender } = renderSuspenseHook( + ({ skip, id }) => useSuspenseQuery(query, { skip, variables: { id } }), + { mocks, link, strictMode: true, initialProps: { skip: true, id: "1" } } + ); + + expect(fetchCount).toBe(0); + + rerender({ skip: false, id: "1" }); + + expect(fetchCount).toBe(1); + + await waitFor(() => { + expect(result.current).toMatchObject({ + ...mocks[0].result, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + }); + + rerender({ skip: true, id: "2" }); + + expect(fetchCount).toBe(1); + }); + it("`skip` result is referentially stable", async () => { const { query, mocks } = useSimpleQueryCase(); @@ -5791,12 +5884,12 @@ describe("useSuspenseQuery", () => { const todo = data?.todo; - return todo ? ( -
- {todo.name} - {todo.completed && " (completed)"} -
- ) : null; + return todo ? +
+ {todo.name} + {todo.completed && " (completed)"} +
+ : null; } render(); @@ -5891,12 +5984,12 @@ describe("useSuspenseQuery", () => { const todo = data?.todo; - return todo ? ( -
- {todo.name} - {todo.completed && " (completed)"} -
- ) : null; + return todo ? +
+ {todo.name} + {todo.completed && " (completed)"} +
+ : null; } render(); @@ -9498,9 +9591,14 @@ describe("useSuspenseQuery", () => { await act(() => user.type(input, "ab")); - await waitFor(() => { - expect(screen.getByTestId("result")).toHaveTextContent("ab"); - }); + await waitFor( + () => { + expect(screen.getByTestId("result")).toHaveTextContent("ab"); + }, + { + timeout: 10000, + } + ); await act(() => user.type(input, "c")); @@ -9519,7 +9617,7 @@ describe("useSuspenseQuery", () => { await waitFor(() => { expect(screen.getByTestId("result")).toHaveTextContent("abc"); }); - }); + }, 10000); it("works with startTransition to change variables", async () => { type Variables = { @@ -9940,6 +10038,539 @@ describe("useSuspenseQuery", () => { }); }); + it("updates networkStatus when a network request returns the same cached data with 'cache-and-network' fetchPolicy", async () => { + const { query } = useSimpleQueryCase(); + + const link = new ApolloLink(() => { + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: "Hello" } }); + observer.complete(); + }, 10); + }); + }); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + // preloaded cache + await client.writeQuery({ query, data: { greeting: "Hello" } }); + + const { result } = renderSuspenseHook( + () => + useSuspenseQuery(query, { + fetchPolicy: "cache-and-network", + }), + { client } + ); + + await waitFor(() => { + // We should see the cached greeting while the network request is in flight + // and the network status should be set to `loading`. + expect(result.current).toMatchObject({ + data: { greeting: "Hello" }, + networkStatus: NetworkStatus.loading, + }); + }); + + await waitFor(() => { + // We should see the updated greeting once the network request finishes + // and the network status should be set to `ready`. + expect(result.current).toMatchObject({ + data: { greeting: "Hello" }, + networkStatus: NetworkStatus.ready, + }); + }); + }); + + // https://github.com/apollographql/apollo-client/issues/11315 + it("fetchMore does not cause extra render", async () => { + const { query, link } = setupPaginatedCase(); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: offsetLimitPagination(), + }, + }, + }, + }), + link, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseSuspenseQueryResult< + PaginatedCaseData, + PaginatedCaseVariables + > | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + + return
Loading...
; + } + + function App() { + useTrackRenders(); + const [isPending, startTransition] = useTransition(); + const result = useSuspenseQuery(query, { + variables: { offset: 0, limit: 2 }, + }); + const { data, fetchMore } = result; + + Profiler.mergeSnapshot({ result }); + + return ( + + ); + } + + render(, { + wrapper: ({ children }) => ( + + + }>{children} + + + ), + }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }); + } + + await act(() => user.click(screen.getByText("Fetch next"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(screen.getByText("Fetch next")).toBeDisabled(); + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + { __typename: "Letter", letter: "E", position: 5 }, + ], + }); + } + + await expect(Profiler).not.toRerender(); + }); + + // https://github.com/apollographql/apollo-client/issues/11708 + it("`fetchMore` works with startTransition when setting errorPolicy as default option in ApolloClient constructor", async () => { + type Variables = { + offset: number; + }; + + interface Todo { + __typename: "Todo"; + id: string; + name: string; + completed: boolean; + } + interface Data { + todos: Todo[]; + } + const user = userEvent.setup(); + + const query: TypedDocumentNode = gql` + query TodosQuery($offset: Int!) { + todos(offset: $offset) { + id + name + completed + } + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query, variables: { offset: 0 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, + }, + delay: 10, + }, + { + request: { query, variables: { offset: 1 } }, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, + }, + delay: 10, + }, + ]; + + const Profiler = createProfiler({ + initialSnapshot: { + isPending: false, + result: null as Pick< + UseSuspenseQueryResult, + "data" | "error" | "networkStatus" + > | null, + }, + }); + + function SuspenseFallback() { + useTrackRenders(); + + return
Loading...
; + } + + const client = new ApolloClient({ + link: new MockLink(mocks), + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + todos: offsetLimitPagination(), + }, + }, + }, + }), + defaultOptions: { + watchQuery: { + errorPolicy: "all", + }, + }, + }); + + function App() { + useTrackRenders(); + const [isPending, startTransition] = React.useTransition(); + const { data, error, networkStatus, fetchMore } = useSuspenseQuery( + query, + { + variables: { offset: 0 }, + } + ); + + Profiler.mergeSnapshot({ + isPending, + result: { data, error, networkStatus }, + }); + + return ( + + ); + } + + render(, { + wrapper: ({ children }) => ( + + + }>{children} + + + ), + }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await act(() => user.click(screen.getByText("Load more"))); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot).toEqual({ + isPending: true, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot).toEqual({ + isPending: false, + result: { + data: { + todos: [ + { + __typename: "Todo", + id: "1", + name: "Clean room", + completed: false, + }, + { + __typename: "Todo", + id: "2", + name: "Take out trash", + completed: true, + }, + ], + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }, + }); + } + + await expect(Profiler).not.toRerender(); + }); + + // https://github.com/apollographql/apollo-client/issues/11642 + it("returns merged array when `fetchMore` returns empty array of results", async () => { + const query: TypedDocumentNode = + gql` + query LettersQuery($limit: Int, $offset: Int) { + letters(limit: $limit, offset: $offset) { + letter + position + } + } + `; + + const data = "ABCD".split("").map((letter, index) => ({ + __typename: "Letter", + letter, + position: index + 1, + })); + + const link = new MockLink([ + { + request: { query, variables: { offset: 0, limit: 2 } }, + result: { data: { letters: data.slice(0, 2) } }, + delay: 20, + }, + { + request: { query, variables: { offset: 2, limit: 2 } }, + result: { data: { letters: data.slice(2, 4) } }, + delay: 20, + }, + { + request: { query, variables: { offset: 4, limit: 2 } }, + result: { data: { letters: [] } }, + delay: 20, + }, + ]); + + const user = userEvent.setup(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + typePolicies: { + Query: { + fields: { + letters: offsetLimitPagination(), + }, + }, + }, + }), + link, + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseSuspenseQueryResult< + PaginatedCaseData, + PaginatedCaseVariables + > | null, + }, + }); + + function App() { + useTrackRenders(); + const result = useSuspenseQuery(query, { + variables: { offset: 0, limit: 2 }, + }); + const { data, fetchMore } = result; + + Profiler.mergeSnapshot({ result }); + + return ( + + ); + } + + render(, { + wrapper: ({ children }) => ( + + + Loading...}>{children} + + + ), + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App]); + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + ], + }); + } + + await act(() => user.click(screen.getByText("Fetch next"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }); + } + + await act(() => user.click(screen.getByText("Fetch next"))); + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + letters: [ + { __typename: "Letter", letter: "A", position: 1 }, + { __typename: "Letter", letter: "B", position: 2 }, + { __typename: "Letter", letter: "C", position: 3 }, + { __typename: "Letter", letter: "D", position: 4 }, + ], + }); + } + + await expect(Profiler).not.toRerender(); + }); + describe.skip("type tests", () => { it("returns unknown when TData cannot be inferred", () => { const query = gql` diff --git a/src/react/hooks/index.ts b/src/react/hooks/index.ts index 61d50665cac..78fc82c61f4 100644 --- a/src/react/hooks/index.ts +++ b/src/react/hooks/index.ts @@ -11,6 +11,13 @@ export type { UseSuspenseQueryResult } from "./useSuspenseQuery.js"; export { useSuspenseQuery } from "./useSuspenseQuery.js"; export type { UseBackgroundQueryResult } from "./useBackgroundQuery.js"; export { useBackgroundQuery } from "./useBackgroundQuery.js"; +export type { + LoadQueryFunction, + UseLoadableQueryResult, +} from "./useLoadableQuery.js"; +export { useLoadableQuery } from "./useLoadableQuery.js"; +export type { UseQueryRefHandlersResult } from "./useQueryRefHandlers.js"; +export { useQueryRefHandlers } from "./useQueryRefHandlers.js"; export type { UseReadQueryResult } from "./useReadQuery.js"; export { useReadQuery } from "./useReadQuery.js"; export { skipToken } from "./constants.js"; diff --git a/src/react/hooks/internal/__tests__/useRenderGuard.test.tsx b/src/react/hooks/internal/__tests__/useRenderGuard.test.tsx new file mode 100644 index 00000000000..0f60cb58892 --- /dev/null +++ b/src/react/hooks/internal/__tests__/useRenderGuard.test.tsx @@ -0,0 +1,36 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +import React, { useEffect } from "rehackt"; +import { useRenderGuard } from "../useRenderGuard"; +import { render, waitFor } from "@testing-library/react"; + +const UNDEF = {}; +const IS_REACT_19 = React.version.startsWith("19"); + +it("returns a function that returns `true` if called during render", () => { + // We don't provide this functionality with React 19 anymore since it requires internals access + if (IS_REACT_19) return; + let result: boolean | typeof UNDEF = UNDEF; + function TestComponent() { + const calledDuringRender = useRenderGuard(); + result = calledDuringRender(); + return <>Test; + } + render(); + expect(result).toBe(true); +}); + +it("returns a function that returns `false` if called after render", async () => { + let result: boolean | typeof UNDEF = UNDEF; + function TestComponent() { + const calledDuringRender = useRenderGuard(); + useEffect(() => { + result = calledDuringRender(); + }); + return <>Test; + } + render(); + await waitFor(() => { + expect(result).not.toBe(UNDEF); + }); + expect(result).toBe(false); +}); diff --git a/src/react/hooks/internal/__use.ts b/src/react/hooks/internal/__use.ts index 2a49fab9e0c..b06760ff04e 100644 --- a/src/react/hooks/internal/__use.ts +++ b/src/react/hooks/internal/__use.ts @@ -1,5 +1,5 @@ import { wrapPromiseWithState } from "../../../utilities/index.js"; -import * as React from "react"; +import * as React from "rehackt"; type Use = (promise: Promise) => T; // Prevent webpack from complaining about our feature detection of the diff --git a/src/react/hooks/internal/index.ts b/src/react/hooks/internal/index.ts index d1c90c41f4a..ce58b546f69 100644 --- a/src/react/hooks/internal/index.ts +++ b/src/react/hooks/internal/index.ts @@ -1,4 +1,7 @@ // These hooks are used internally and are not exported publicly by the library export { useDeepMemo } from "./useDeepMemo.js"; export { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js"; +export { useRenderGuard } from "./useRenderGuard.js"; +export { useLazyRef } from "./useLazyRef.js"; export { __use } from "./__use.js"; +export { wrapHook } from "./wrapHook.js"; diff --git a/src/react/hooks/internal/useDeepMemo.ts b/src/react/hooks/internal/useDeepMemo.ts index 5a49115a49b..916e23a746d 100644 --- a/src/react/hooks/internal/useDeepMemo.ts +++ b/src/react/hooks/internal/useDeepMemo.ts @@ -1,5 +1,5 @@ import type { DependencyList } from "react"; -import * as React from "react"; +import * as React from "rehackt"; import { equal } from "@wry/equality"; export function useDeepMemo( diff --git a/src/react/hooks/internal/useIsomorphicLayoutEffect.ts b/src/react/hooks/internal/useIsomorphicLayoutEffect.ts index f5380a88fdd..d5528cfde17 100644 --- a/src/react/hooks/internal/useIsomorphicLayoutEffect.ts +++ b/src/react/hooks/internal/useIsomorphicLayoutEffect.ts @@ -1,4 +1,4 @@ -import * as React from "react"; +import * as React from "rehackt"; import { canUseDOM } from "../../../utilities/index.js"; // use canUseDOM here instead of canUseLayoutEffect because we want to be able @@ -6,6 +6,5 @@ import { canUseDOM } from "../../../utilities/index.js"; // in useSuspenseQuery tests, but to honor the original comment about the // warnings for useSyncExternalStore implementation, canUseLayoutEffect is left // alone. -export const useIsomorphicLayoutEffect = canUseDOM - ? React.useLayoutEffect - : React.useEffect; +export const useIsomorphicLayoutEffect = + canUseDOM ? React.useLayoutEffect : React.useEffect; diff --git a/src/react/hooks/internal/useLazyRef.ts b/src/react/hooks/internal/useLazyRef.ts new file mode 100644 index 00000000000..2656a2773a4 --- /dev/null +++ b/src/react/hooks/internal/useLazyRef.ts @@ -0,0 +1,13 @@ +import * as React from "rehackt"; + +const INIT = {}; + +export function useLazyRef(getInitialValue: () => T) { + const ref = React.useRef(INIT as unknown as T); + + if (ref.current === INIT) { + ref.current = getInitialValue(); + } + + return ref; +} diff --git a/src/react/hooks/internal/useRenderGuard.ts b/src/react/hooks/internal/useRenderGuard.ts new file mode 100644 index 00000000000..ba101f109b7 --- /dev/null +++ b/src/react/hooks/internal/useRenderGuard.ts @@ -0,0 +1,45 @@ +import * as React from "rehackt"; + +let Ctx: React.Context; + +function noop() {} +export function useRenderGuard() { + if (!Ctx) { + // we want the intialization to be lazy because `createContext` would error on import in a RSC + Ctx = React.createContext(null); + } + + return React.useCallback( + /** + * @returns true if the hook was called during render + */ () => { + const orig = console.error; + try { + console.error = noop; + + /** + * `useContext` can be called conditionally during render, so this is safe. + * (Also, during render we would want to throw as a reaction to this anyways, so it + * wouldn't even matter if we got the order of hooks mixed up...) + * + * They cannot however be called outside of Render, and that's what we're testing here. + * + * Different versions of React have different behaviour on an invalid hook call: + * + * React 16.8 - 17: throws an error + * https://github.com/facebook/react/blob/2b93d686e359c7afa299e2ec5cf63160a32a1155/packages/react/src/ReactHooks.js#L18-L26 + * + * React 18 & 19: `console.error` in development, then `resolveDispatcher` returns `null` and a member access on `null` throws. + * https://github.com/facebook/react/blob/58e8304483ebfadd02a295339b5e9a989ac98c6e/packages/react/src/ReactHooks.js#L28-L35 + */ + React["useContext" /* hide this from the linter */](Ctx); + return true; + } catch (e) { + return false; + } finally { + console.error = orig; + } + }, + [] + ); +} diff --git a/src/react/hooks/internal/wrapHook.ts b/src/react/hooks/internal/wrapHook.ts new file mode 100644 index 00000000000..c22ec726e9d --- /dev/null +++ b/src/react/hooks/internal/wrapHook.ts @@ -0,0 +1,90 @@ +import type { + useQuery, + useSuspenseQuery, + useBackgroundQuery, + useReadQuery, + useFragment, + useQueryRefHandlers, +} from "../index.js"; +import type { QueryManager } from "../../../core/QueryManager.js"; +import type { ApolloClient } from "../../../core/ApolloClient.js"; +import type { ObservableQuery } from "../../../core/ObservableQuery.js"; + +const wrapperSymbol = Symbol.for("apollo.hook.wrappers"); + +interface WrappableHooks { + useQuery: typeof useQuery; + useSuspenseQuery: typeof useSuspenseQuery; + useBackgroundQuery: typeof useBackgroundQuery; + useReadQuery: typeof useReadQuery; + useFragment: typeof useFragment; + useQueryRefHandlers: typeof useQueryRefHandlers; +} + +/** + * @internal + * Can be used to correctly type the [Symbol.for("apollo.hook.wrappers")] property of + * `QueryManager`, to override/wrap hook functionality. + */ +export type HookWrappers = { + [K in keyof WrappableHooks]?: ( + originalHook: WrappableHooks[K] + ) => WrappableHooks[K]; +}; + +interface QueryManagerWithWrappers extends QueryManager { + [wrapperSymbol]?: HookWrappers; +} + +/** + * @internal + * + * Makes an Apollo Client hook "wrappable". + * That means that the Apollo Client instance can expose a "wrapper" that will be + * used to wrap the original hook implementation with additional logic. + * @example + * ```tsx + * // this is already done in `@apollo/client` for all wrappable hooks (see `WrappableHooks`) + * // following this pattern + * function useQuery() { + * return wrapHook('useQuery', _useQuery, options.client)(query, options); + * } + * function _useQuery(query, options) { + * // original implementation + * } + * + * // this is what a library like `@apollo/client-react-streaming` would do + * class ApolloClientWithStreaming extends ApolloClient { + * constructor(options) { + * super(options); + * this.queryManager[Symbol.for("apollo.hook.wrappers")] = { + * useQuery: (original) => (query, options) => { + * console.log("useQuery was called with options", options); + * return original(query, options); + * } + * } + * } + * } + * + * // this will now log the options and then call the original `useQuery` + * const client = new ApolloClientWithStreaming({ ... }); + * useQuery(query, { client }); + * ``` + */ +export function wrapHook any>( + hookName: keyof WrappableHooks, + useHook: Hook, + clientOrObsQuery: ObservableQuery | ApolloClient +): Hook { + const queryManager = ( + clientOrObsQuery as unknown as { + // both `ApolloClient` and `ObservableQuery` have a `queryManager` property + // but they're both `private`, so we have to cast around for a bit here. + queryManager: QueryManagerWithWrappers; + } + )["queryManager"]; + const wrappers = queryManager && queryManager[wrapperSymbol]; + const wrapper: undefined | ((wrap: Hook) => Hook) = + wrappers && (wrappers[hookName] as any); + return wrapper ? wrapper(useHook) : useHook; +} diff --git a/src/react/hooks/useApolloClient.ts b/src/react/hooks/useApolloClient.ts index c9e81a8551c..8f4fdc80f0c 100644 --- a/src/react/hooks/useApolloClient.ts +++ b/src/react/hooks/useApolloClient.ts @@ -1,8 +1,23 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; import type { ApolloClient } from "../../core/index.js"; import { getApolloContext } from "../context/index.js"; +/** + * @example + * ```jsx + * import { useApolloClient } from '@apollo/client'; + * + * function SomeComponent() { + * const client = useApolloClient(); + * // `client` is now set to the `ApolloClient` instance being used by the + * // application (that was configured using something like `ApolloProvider`) + * } + * ``` + * + * @since 3.0.0 + * @returns The `ApolloClient` instance being used by the application. + */ export function useApolloClient( override?: ApolloClient ): ApolloClient { diff --git a/src/react/hooks/useBackgroundQuery.ts b/src/react/hooks/useBackgroundQuery.ts index 8e49114e7aa..4b5a5668389 100644 --- a/src/react/hooks/useBackgroundQuery.ts +++ b/src/react/hooks/useBackgroundQuery.ts @@ -1,20 +1,25 @@ -import * as React from "react"; +import * as React from "rehackt"; import type { DocumentNode, + FetchMoreQueryOptions, OperationVariables, TypedDocumentNode, + WatchQueryOptions, } from "../../core/index.js"; import { useApolloClient } from "./useApolloClient.js"; -import { wrapQueryRef } from "../cache/QueryReference.js"; -import type { QueryReference } from "../cache/QueryReference.js"; +import { + getSuspenseCache, + unwrapQueryRef, + updateWrappedQueryRef, + wrapQueryRef, +} from "../internal/index.js"; +import type { CacheKey, QueryRef } from "../internal/index.js"; import type { BackgroundQueryHookOptions, NoInfer } from "../types/types.js"; -import { __use } from "./internal/index.js"; -import { getSuspenseCache } from "../cache/index.js"; +import { wrapHook } from "./internal/index.js"; import { useWatchQueryOptions } from "./useSuspenseQuery.js"; import type { FetchMoreFunction, RefetchFunction } from "./useSuspenseQuery.js"; import { canonicalStringify } from "../../cache/index.js"; import type { DeepPartial } from "../../utilities/index.js"; -import type { CacheKey } from "../cache/types.js"; import type { SkipToken } from "./constants.js"; export type UseBackgroundQueryResult< @@ -39,14 +44,14 @@ export function useBackgroundQuery< options?: BackgroundQueryHookOptionsNoInfer & TOptions ): [ ( - | QueryReference< - TOptions["errorPolicy"] extends "ignore" | "all" - ? TOptions["returnPartialData"] extends true - ? DeepPartial | undefined - : TData | undefined - : TOptions["returnPartialData"] extends true - ? DeepPartial - : TData + | QueryRef< + TOptions["errorPolicy"] extends "ignore" | "all" ? + TOptions["returnPartialData"] extends true ? + DeepPartial | undefined + : TData | undefined + : TOptions["returnPartialData"] extends true ? DeepPartial + : TData, + TVariables > | (TOptions["skip"] extends boolean ? undefined : never) ), @@ -63,7 +68,7 @@ export function useBackgroundQuery< errorPolicy: "ignore" | "all"; } ): [ - QueryReference | undefined>, + QueryRef | undefined, TVariables>, UseBackgroundQueryResult, ]; @@ -76,7 +81,7 @@ export function useBackgroundQuery< errorPolicy: "ignore" | "all"; } ): [ - QueryReference, + QueryRef, UseBackgroundQueryResult, ]; @@ -90,7 +95,7 @@ export function useBackgroundQuery< returnPartialData: true; } ): [ - QueryReference> | undefined, + QueryRef, TVariables> | undefined, UseBackgroundQueryResult, ]; @@ -103,7 +108,7 @@ export function useBackgroundQuery< returnPartialData: true; } ): [ - QueryReference>, + QueryRef, TVariables>, UseBackgroundQueryResult, ]; @@ -116,7 +121,7 @@ export function useBackgroundQuery< skip: boolean; } ): [ - QueryReference | undefined, + QueryRef | undefined, UseBackgroundQueryResult, ]; @@ -126,7 +131,7 @@ export function useBackgroundQuery< >( query: DocumentNode | TypedDocumentNode, options?: BackgroundQueryHookOptionsNoInfer -): [QueryReference, UseBackgroundQueryResult]; +): [QueryRef, UseBackgroundQueryResult]; export function useBackgroundQuery< TData = unknown, @@ -147,7 +152,7 @@ export function useBackgroundQuery< returnPartialData: true; }) ): [ - QueryReference> | undefined, + QueryRef, TVariables> | undefined, UseBackgroundQueryResult, ]; @@ -158,7 +163,7 @@ export function useBackgroundQuery< query: DocumentNode | TypedDocumentNode, options?: SkipToken | BackgroundQueryHookOptionsNoInfer ): [ - QueryReference | undefined, + QueryRef | undefined, UseBackgroundQueryResult, ]; @@ -172,7 +177,27 @@ export function useBackgroundQuery< Partial>) | BackgroundQueryHookOptionsNoInfer = Object.create(null) ): [ - QueryReference | undefined, + QueryRef | undefined, + UseBackgroundQueryResult, +] { + return wrapHook( + "useBackgroundQuery", + _useBackgroundQuery, + useApolloClient(typeof options === "object" ? options.client : undefined) + )(query, options); +} + +function _useBackgroundQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: + | (SkipToken & + Partial>) + | BackgroundQueryHookOptionsNoInfer +): [ + QueryRef | undefined, UseBackgroundQueryResult, ] { const client = useApolloClient(options.client); @@ -197,27 +222,44 @@ export function useBackgroundQuery< ]; const queryRef = suspenseCache.getQueryRef(cacheKey, () => - client.watchQuery(watchQueryOptions) + client.watchQuery(watchQueryOptions as WatchQueryOptions) ); - const [promiseCache, setPromiseCache] = React.useState( - () => new Map([[queryRef.key, queryRef.promise]]) + const [wrappedQueryRef, setWrappedQueryRef] = React.useState( + wrapQueryRef(queryRef) ); - + if (unwrapQueryRef(wrappedQueryRef) !== queryRef) { + setWrappedQueryRef(wrapQueryRef(queryRef)); + } if (queryRef.didChangeOptions(watchQueryOptions)) { const promise = queryRef.applyOptions(watchQueryOptions); - promiseCache.set(queryRef.key, promise); + updateWrappedQueryRef(wrappedQueryRef, promise); } - React.useEffect(() => queryRef.retain(), [queryRef]); + // This prevents issues where rerendering useBackgroundQuery after the + // queryRef has been disposed would cause the hook to return a new queryRef + // instance since disposal also removes it from the suspense cache. We add + // the queryRef back in the suspense cache so that the next render will reuse + // this queryRef rather than initializing a new instance. + React.useEffect(() => { + // Since the queryRef is disposed async via `setTimeout`, we have to wait a + // tick before checking it and adding back to the suspense cache. + const id = setTimeout(() => { + if (queryRef.disposed) { + suspenseCache.add(cacheKey, queryRef); + } + }); + + return () => clearTimeout(id); + // Omitting the deps is intentional. This avoids stale closures and the + // conditional ensures we aren't running the logic on each render. + }); const fetchMore: FetchMoreFunction = React.useCallback( (options) => { - const promise = queryRef.fetchMore(options); + const promise = queryRef.fetchMore(options as FetchMoreQueryOptions); - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, queryRef.promise) - ); + setWrappedQueryRef(wrapQueryRef(queryRef)); return promise; }, @@ -228,21 +270,14 @@ export function useBackgroundQuery< (variables) => { const promise = queryRef.refetch(variables); - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, queryRef.promise) - ); + setWrappedQueryRef(wrapQueryRef(queryRef)); return promise; }, [queryRef] ); - queryRef.promiseCache = promiseCache; - - const wrappedQueryRef = React.useMemo( - () => wrapQueryRef(queryRef), - [queryRef] - ); + React.useEffect(() => queryRef.softRetain(), [queryRef]); return [ didFetchResult.current ? wrappedQueryRef : void 0, diff --git a/src/react/hooks/useFragment.ts b/src/react/hooks/useFragment.ts index debd1bd02eb..6f94e8edaac 100644 --- a/src/react/hooks/useFragment.ts +++ b/src/react/hooks/useFragment.ts @@ -1,6 +1,4 @@ -import * as React from "react"; -import { equal } from "@wry/equality"; - +import * as React from "rehackt"; import type { DeepPartial } from "../../utilities/index.js"; import { mergeDeepArray } from "../../utilities/index.js"; import type { @@ -12,8 +10,10 @@ import type { import { useApolloClient } from "./useApolloClient.js"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; -import type { OperationVariables } from "../../core/index.js"; +import type { ApolloClient, OperationVariables } from "../../core/index.js"; import type { NoInfer } from "../types/types.js"; +import { useDeepMemo, useLazyRef, wrapHook } from "./internal/index.js"; +import equal from "@wry/equality"; export interface UseFragmentOptions extends Omit< @@ -27,6 +27,15 @@ export interface UseFragmentOptions from: StoreObject | Reference | string; // Override this field to make it optional (default: true). optimistic?: boolean; + /** + * The instance of `ApolloClient` to use to look up the fragment. + * + * By default, the instance that's passed down via context is used, but you + * can provide a different instance here. + * + * @docGroup 1. Operation options + */ + client?: ApolloClient; } export type UseFragmentResult = @@ -44,48 +53,74 @@ export type UseFragmentResult = export function useFragment( options: UseFragmentOptions ): UseFragmentResult { - const { cache } = useApolloClient(); + return wrapHook( + "useFragment", + _useFragment, + useApolloClient(options.client) + )(options); +} + +function _useFragment( + options: UseFragmentOptions +): UseFragmentResult { + const { cache } = useApolloClient(options.client); - const { fragment, fragmentName, from, optimistic = true, ...rest } = options; + const diffOptions = useDeepMemo>(() => { + const { + fragment, + fragmentName, + from, + optimistic = true, + ...rest + } = options; + + return { + ...rest, + returnPartialData: true, + id: typeof from === "string" ? from : cache.identify(from), + query: cache["getFragmentDoc"](fragment, fragmentName), + optimistic, + }; + }, [options]); + + const resultRef = useLazyRef>(() => + diffToResult(cache.diff(diffOptions)) + ); - const diffOptions: Cache.DiffOptions = { - ...rest, - returnPartialData: true, - id: typeof from === "string" ? from : cache.identify(from), - query: cache["getFragmentDoc"](fragment, fragmentName), - optimistic, - }; + const stableOptions = useDeepMemo(() => options, [options]); - const resultRef = React.useRef>(); - let latestDiff = cache.diff(diffOptions); + // Since .next is async, we need to make sure that we + // get the correct diff on the next render given new diffOptions + React.useMemo(() => { + resultRef.current = diffToResult(cache.diff(diffOptions)); + }, [diffOptions, cache]); // Used for both getSnapshot and getServerSnapshot - const getSnapshot = () => { - const latestDiffToResult = diffToResult(latestDiff); - return resultRef.current && - equal(resultRef.current.data, latestDiffToResult.data) - ? resultRef.current - : (resultRef.current = latestDiffToResult); - }; + const getSnapshot = React.useCallback(() => resultRef.current, []); return useSyncExternalStore( - (forceUpdate) => { - let lastTimeout = 0; - const unsubcribe = cache.watch({ - ...diffOptions, - immediate: true, - callback(diff) { - if (!equal(diff, latestDiff)) { - resultRef.current = diffToResult((latestDiff = diff)); + React.useCallback( + (forceUpdate) => { + let lastTimeout = 0; + const subscription = cache.watchFragment(stableOptions).subscribe({ + next: (result) => { + if (equal(result, resultRef.current)) return; + resultRef.current = result; + // If we get another update before we've re-rendered, bail out of + // the update and try again. This ensures that the relative timing + // between useQuery and useFragment stays roughly the same as + // fixed in https://github.com/apollographql/apollo-client/pull/11083 + clearTimeout(lastTimeout); lastTimeout = setTimeout(forceUpdate) as any; - } - }, - }); - return () => { - unsubcribe(); - clearTimeout(lastTimeout); - }; - }, + }, + }); + return () => { + subscription.unsubscribe(); + clearTimeout(lastTimeout); + }; + }, + [cache, stableOptions] + ), getSnapshot, getSnapshot ); diff --git a/src/react/hooks/useLazyQuery.ts b/src/react/hooks/useLazyQuery.ts index 535b9b4ceb2..a8d6eb00a67 100644 --- a/src/react/hooks/useLazyQuery.ts +++ b/src/react/hooks/useLazyQuery.ts @@ -1,6 +1,6 @@ import type { DocumentNode } from "graphql"; import type { TypedDocumentNode } from "@graphql-typed-document-node/core"; -import * as React from "react"; +import * as React from "rehackt"; import type { OperationVariables } from "../../core/index.js"; import { mergeOptions } from "../../utilities/index.js"; @@ -9,7 +9,6 @@ import type { LazyQueryHookOptions, LazyQueryResultTuple, NoInfer, - QueryResult, } from "../types/types.js"; import { useInternalState } from "./useQuery.js"; import { useApolloClient } from "./useApolloClient.js"; @@ -25,6 +24,41 @@ const EAGER_METHODS = [ "subscribeToMore", ] as const; +/** + * A hook for imperatively executing queries in an Apollo application, e.g. in response to user interaction. + * + * > Refer to the [Queries - Manual execution with useLazyQuery](https://www.apollographql.com/docs/react/data/queries#manual-execution-with-uselazyquery) section for a more in-depth overview of `useLazyQuery`. + * + * @example + * ```jsx + * import { gql, useLazyQuery } from "@apollo/client"; + * + * const GET_GREETING = gql` + * query GetGreeting($language: String!) { + * greeting(language: $language) { + * message + * } + * } + * `; + * + * function Hello() { + * const [loadGreeting, { called, loading, data }] = useLazyQuery( + * GET_GREETING, + * { variables: { language: "english" } } + * ); + * if (called && loading) return

Loading ...

+ * if (!called) { + * return + * } + * return

Hello {data.greeting.message}!

; + * } + * ``` + * @since 3.0.0 + * + * @param query - A GraphQL query document parsed into an AST by `gql`. + * @param options - Default options to control how the query is executed. + * @returns A tuple in the form of `[execute, result]` + */ export function useLazyQuery< TData = any, TVariables extends OperationVariables = OperationVariables, @@ -43,7 +77,7 @@ export function useLazyQuery< // Use refs to track options and the used query to ensure the `execute` // function remains referentially stable between renders. - optionsRef.current = merged; + optionsRef.current = options; queryRef.current = document; const internalState = useInternalState( @@ -60,34 +94,41 @@ export function useLazyQuery< useQueryResult.observable.options.initialFetchPolicy || internalState.getDefaultFetchPolicy(); - const result: QueryResult = Object.assign(useQueryResult, { - called: !!execOptionsRef.current, - }); - + const { forceUpdateState, obsQueryFields } = internalState; // We use useMemo here to make sure the eager methods have a stable identity. const eagerMethods = React.useMemo(() => { const eagerMethods: Record = {}; for (const key of EAGER_METHODS) { - const method = result[key]; + const method = obsQueryFields[key]; eagerMethods[key] = function () { if (!execOptionsRef.current) { execOptionsRef.current = Object.create(null); // Only the first time populating execOptionsRef.current matters here. - internalState.forceUpdateState(); + forceUpdateState(); } + // @ts-expect-error this is just too generic to type return method.apply(this, arguments); }; } return eagerMethods; - }, []); - - Object.assign(result, eagerMethods); + }, [forceUpdateState, obsQueryFields]); + + const called = !!execOptionsRef.current; + const result = React.useMemo( + () => ({ + ...useQueryResult, + ...eagerMethods, + called, + }), + [useQueryResult, eagerMethods, called] + ); const execute = React.useCallback[0]>( (executeOptions) => { - execOptionsRef.current = executeOptions - ? { + execOptionsRef.current = + executeOptions ? + { ...executeOptions, fetchPolicy: executeOptions.fetchPolicy || initialFetchPolicy, } @@ -110,7 +151,7 @@ export function useLazyQuery< return promise; }, - [] + [eagerMethods, initialFetchPolicy, internalState] ); return [execute, result]; diff --git a/src/react/hooks/useLoadableQuery.ts b/src/react/hooks/useLoadableQuery.ts new file mode 100644 index 00000000000..15d1e2a7e56 --- /dev/null +++ b/src/react/hooks/useLoadableQuery.ts @@ -0,0 +1,263 @@ +import * as React from "rehackt"; +import type { + DocumentNode, + FetchMoreQueryOptions, + OperationVariables, + TypedDocumentNode, + WatchQueryOptions, +} from "../../core/index.js"; +import { useApolloClient } from "./useApolloClient.js"; +import { + assertWrappedQueryRef, + getSuspenseCache, + unwrapQueryRef, + updateWrappedQueryRef, + wrapQueryRef, +} from "../internal/index.js"; +import type { CacheKey, QueryRef } from "../internal/index.js"; +import type { LoadableQueryHookOptions } from "../types/types.js"; +import { __use, useRenderGuard } from "./internal/index.js"; +import { useWatchQueryOptions } from "./useSuspenseQuery.js"; +import type { FetchMoreFunction, RefetchFunction } from "./useSuspenseQuery.js"; +import { canonicalStringify } from "../../cache/index.js"; +import type { + DeepPartial, + OnlyRequiredProperties, +} from "../../utilities/index.js"; +import { invariant } from "../../utilities/globals/index.js"; + +export type LoadQueryFunction = ( + // Use variadic args to handle cases where TVariables is type `never`, in + // which case we don't want to allow a variables argument. In other + // words, we don't want to allow variables to be passed as an argument to this + // function if the query does not expect variables in the document. + ...args: [TVariables] extends [never] ? [] + : {} extends OnlyRequiredProperties ? [variables?: TVariables] + : [variables: TVariables] +) => void; + +type ResetFunction = () => void; + +export type UseLoadableQueryResult< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +> = [ + loadQuery: LoadQueryFunction, + queryRef: QueryRef | null, + handlers: { + /** {@inheritDoc @apollo/client!QueryResultDocumentation#fetchMore:member} */ + fetchMore: FetchMoreFunction; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#refetch:member} */ + refetch: RefetchFunction; + /** + * A function that resets the `queryRef` back to `null`. + */ + reset: ResetFunction; + }, +]; + +export function useLoadableQuery< + TData, + TVariables extends OperationVariables, + TOptions extends LoadableQueryHookOptions, +>( + query: DocumentNode | TypedDocumentNode, + options?: LoadableQueryHookOptions & TOptions +): UseLoadableQueryResult< + TOptions["errorPolicy"] extends "ignore" | "all" ? + TOptions["returnPartialData"] extends true ? + DeepPartial | undefined + : TData | undefined + : TOptions["returnPartialData"] extends true ? DeepPartial + : TData, + TVariables +>; + +export function useLoadableQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: LoadableQueryHookOptions & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; + } +): UseLoadableQueryResult | undefined, TVariables>; + +export function useLoadableQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: LoadableQueryHookOptions & { + errorPolicy: "ignore" | "all"; + } +): UseLoadableQueryResult; + +export function useLoadableQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: LoadableQueryHookOptions & { + returnPartialData: true; + } +): UseLoadableQueryResult, TVariables>; + +/** + * A hook for imperatively loading a query, such as responding to a user + * interaction. + * + * > Refer to the [Suspense - Fetching in response to user interaction](https://www.apollographql.com/docs/react/data/suspense#fetching-in-response-to-user-interaction) section for a more in-depth overview of `useLoadableQuery`. + * + * @example + * ```jsx + * import { gql, useLoadableQuery } from "@apollo/client"; + * + * const GET_GREETING = gql` + * query GetGreeting($language: String!) { + * greeting(language: $language) { + * message + * } + * } + * `; + * + * function App() { + * const [loadGreeting, queryRef] = useLoadableQuery(GET_GREETING); + * + * return ( + * <> + * + * Loading...}> + * {queryRef && } + * + * + * ); + * } + * + * function Hello({ queryRef }) { + * const { data } = useReadQuery(queryRef); + * + * return
{data.greeting.message}
; + * } + * ``` + * + * @since 3.9.0 + * @param query - A GraphQL query document parsed into an AST by `gql`. + * @param options - Options to control how the query is executed. + * @returns A tuple in the form of `[loadQuery, queryRef, handlers]` + */ +export function useLoadableQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options?: LoadableQueryHookOptions +): UseLoadableQueryResult; + +export function useLoadableQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: LoadableQueryHookOptions = Object.create(null) +): UseLoadableQueryResult { + const client = useApolloClient(options.client); + const suspenseCache = getSuspenseCache(client); + const watchQueryOptions = useWatchQueryOptions({ client, query, options }); + const { queryKey = [] } = options; + + const [queryRef, setQueryRef] = React.useState | null>(null); + + assertWrappedQueryRef(queryRef); + + const internalQueryRef = queryRef && unwrapQueryRef(queryRef); + + if (queryRef && internalQueryRef?.didChangeOptions(watchQueryOptions)) { + const promise = internalQueryRef.applyOptions(watchQueryOptions); + updateWrappedQueryRef(queryRef, promise); + } + + const calledDuringRender = useRenderGuard(); + + const fetchMore: FetchMoreFunction = React.useCallback( + (options) => { + if (!internalQueryRef) { + throw new Error( + "The query has not been loaded. Please load the query." + ); + } + + const promise = internalQueryRef.fetchMore( + options as FetchMoreQueryOptions + ); + + setQueryRef(wrapQueryRef(internalQueryRef)); + + return promise; + }, + [internalQueryRef] + ); + + const refetch: RefetchFunction = React.useCallback( + (options) => { + if (!internalQueryRef) { + throw new Error( + "The query has not been loaded. Please load the query." + ); + } + + const promise = internalQueryRef.refetch(options); + + setQueryRef(wrapQueryRef(internalQueryRef)); + + return promise; + }, + [internalQueryRef] + ); + + const loadQuery: LoadQueryFunction = React.useCallback( + (...args) => { + invariant( + !calledDuringRender(), + "useLoadableQuery: 'loadQuery' should not be called during render. To start a query during render, use the 'useBackgroundQuery' hook." + ); + + const [variables] = args; + + const cacheKey: CacheKey = [ + query, + canonicalStringify(variables), + ...([] as any[]).concat(queryKey), + ]; + + const queryRef = suspenseCache.getQueryRef(cacheKey, () => + client.watchQuery({ + ...watchQueryOptions, + variables, + } as WatchQueryOptions) + ); + + setQueryRef(wrapQueryRef(queryRef)); + }, + [ + query, + queryKey, + suspenseCache, + watchQueryOptions, + calledDuringRender, + client, + ] + ); + + const reset: ResetFunction = React.useCallback(() => { + setQueryRef(null); + }, []); + + return [loadQuery, queryRef, { fetchMore, refetch, reset }]; +} diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts index f3c2b233abd..65f0820e015 100644 --- a/src/react/hooks/useMutation.ts +++ b/src/react/hooks/useMutation.ts @@ -1,4 +1,4 @@ -import * as React from "react"; +import * as React from "rehackt"; import type { DocumentNode } from "graphql"; import type { TypedDocumentNode } from "@graphql-typed-document-node/core"; import type { @@ -12,6 +12,7 @@ import type { import type { ApolloCache, DefaultContext, + MutationOptions, OperationVariables, } from "../../core/index.js"; import { mergeOptions } from "../../utilities/index.js"; @@ -20,6 +21,53 @@ import { DocumentType, verifyDocumentType } from "../parser/index.js"; import { ApolloError } from "../../errors/index.js"; import { useApolloClient } from "./useApolloClient.js"; +/** + * + * + * > Refer to the [Mutations](https://www.apollographql.com/docs/react/data/mutations/) section for a more in-depth overview of `useMutation`. + * + * @example + * ```jsx + * import { gql, useMutation } from '@apollo/client'; + * + * const ADD_TODO = gql` + * mutation AddTodo($type: String!) { + * addTodo(type: $type) { + * id + * type + * } + * } + * `; + * + * function AddTodo() { + * let input; + * const [addTodo, { data }] = useMutation(ADD_TODO); + * + * return ( + *
+ *
{ + * e.preventDefault(); + * addTodo({ variables: { type: input.value } }); + * input.value = ''; + * }} + * > + * { + * input = node; + * }} + * /> + * + *
+ *
+ * ); + * } + * ``` + * @since 3.0.0 + * @param mutation - A GraphQL mutation document parsed into an AST by `gql`. + * @param options - Options to control how the mutation is executed. + * @returns A tuple in the form of `[mutate, result]` + */ export function useMutation< TData = any, TVariables = OperationVariables, @@ -51,11 +99,9 @@ export function useMutation< options, }); - // TODO: Trying to assign these in a useEffect or useLayoutEffect breaks - // higher-order components. - { + React.useLayoutEffect(() => { Object.assign(ref.current, { client, options, mutation }); - } + }); const execute = React.useCallback( ( @@ -87,22 +133,25 @@ export function useMutation< } const mutationId = ++ref.current.mutationId; - const clientOptions = mergeOptions(baseOptions, executeOptions as any); + const clientOptions = mergeOptions(baseOptions, executeOptions); return client - .mutate(clientOptions) + .mutate(clientOptions as MutationOptions) .then((response) => { const { data, errors } = response; const error = - errors && errors.length > 0 - ? new ApolloError({ graphQLErrors: errors }) - : void 0; + errors && errors.length > 0 ? + new ApolloError({ graphQLErrors: errors }) + : void 0; const onError = executeOptions.onError || ref.current.options?.onError; if (error && onError) { - onError(error, clientOptions); + onError( + error, + clientOptions as MutationOptions + ); } if ( @@ -126,7 +175,10 @@ export function useMutation< executeOptions.onCompleted || ref.current.options?.onCompleted; if (!error) { - onCompleted?.(response.data!, clientOptions); + onCompleted?.( + response.data!, + clientOptions as MutationOptions + ); } return response; @@ -150,7 +202,10 @@ export function useMutation< executeOptions.onError || ref.current.options?.onError; if (onError) { - onError(error, clientOptions); + onError( + error, + clientOptions as MutationOptions + ); // TODO(brian): why are we returning this here??? return { data: void 0, errors: error }; @@ -164,15 +219,22 @@ export function useMutation< const reset = React.useCallback(() => { if (ref.current.isMounted) { - setResult({ called: false, loading: false, client }); + const result = { + called: false, + loading: false, + client: ref.current.client, + }; + Object.assign(ref.current, { mutationId: 0, result }); + setResult(result); } }, []); React.useEffect(() => { - ref.current.isMounted = true; + const current = ref.current; + current.isMounted = true; return () => { - ref.current.isMounted = false; + current.isMounted = false; }; }, []); diff --git a/src/react/hooks/useQuery.ts b/src/react/hooks/useQuery.ts index 33b127f62e6..61ca66527b6 100644 --- a/src/react/hooks/useQuery.ts +++ b/src/react/hooks/useQuery.ts @@ -1,6 +1,6 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; import { equal } from "@wry/equality"; @@ -36,11 +36,46 @@ import { isNonEmptyArray, maybeDeepFreeze, } from "../../utilities/index.js"; +import { wrapHook } from "./internal/index.js"; const { prototype: { hasOwnProperty }, } = Object; +/** + * A hook for executing queries in an Apollo application. + * + * To run a query within a React component, call `useQuery` and pass it a GraphQL query document. + * + * When your component renders, `useQuery` returns an object from Apollo Client that contains `loading`, `error`, and `data` properties you can use to render your UI. + * + * > Refer to the [Queries](https://www.apollographql.com/docs/react/data/queries) section for a more in-depth overview of `useQuery`. + * + * @example + * ```jsx + * import { gql, useQuery } from '@apollo/client'; + * + * const GET_GREETING = gql` + * query GetGreeting($language: String!) { + * greeting(language: $language) { + * message + * } + * } + * `; + * + * function Hello() { + * const { loading, error, data } = useQuery(GET_GREETING, { + * variables: { language: 'english' }, + * }); + * if (loading) return

Loading ...

; + * return

Hello {data.greeting.message}!

; + * } + * ``` + * @since 3.0.0 + * @param query - A GraphQL query document parsed into an AST by `gql`. + * @param options - Options to control how the query is executed. + * @returns Query result object + */ export function useQuery< TData = any, TVariables extends OperationVariables = OperationVariables, @@ -51,6 +86,20 @@ export function useQuery< NoInfer > = Object.create(null) ): QueryResult { + return wrapHook( + "useQuery", + _useQuery, + useApolloClient(options && options.client) + )(query, options); +} + +function _useQuery< + TData = any, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: QueryHookOptions, NoInfer> +) { return useInternalState(useApolloClient(options.client), query).useQuery( options ); @@ -60,23 +109,30 @@ export function useInternalState( client: ApolloClient, query: DocumentNode | TypedDocumentNode ): InternalState { - const stateRef = React.useRef>(); - if ( - !stateRef.current || - client !== stateRef.current.client || - query !== stateRef.current.query - ) { - stateRef.current = new InternalState(client, query, stateRef.current); - } - const state = stateRef.current; - // By default, InternalState.prototype.forceUpdate is an empty function, but // we replace it here (before anyone has had a chance to see this state yet) // with a function that unconditionally forces an update, using the latest - // setTick function. Updating this state by calling state.forceUpdate is the - // only way we trigger React component updates (no other useState calls within - // the InternalState class). - state.forceUpdateState = React.useReducer((tick) => tick + 1, 0)[1]; + // setTick function. Updating this state by calling state.forceUpdate or the + // uSES notification callback are the only way we trigger React component updates. + const forceUpdateState = React.useReducer((tick) => tick + 1, 0)[1]; + + function createInternalState(previous?: InternalState) { + return Object.assign(new InternalState(client, query, previous), { + forceUpdateState, + }); + } + + let [state, updateState] = React.useState(createInternalState); + + if (client !== state.client || query !== state.query) { + // If the client or query have changed, we need to create a new InternalState. + // This will trigger a re-render with the new state, but it will also continue + // to run the current render function to completion. + // Since we sometimes trigger some side-effects in the render function, we + // re-assign `state` to the new state to ensure that those side-effects are + // triggered with the new state. + updateState((state = createInternalState(state))); + } return state; } @@ -174,13 +230,16 @@ class InternalState { // initialization, this.renderPromises is usually undefined (unless SSR is // happening), but that's fine as long as it has been initialized that way, // rather than left uninitialized. + // eslint-disable-next-line react-hooks/rules-of-hooks this.renderPromises = React.useContext(getApolloContext()).renderPromises; this.useOptions(options); const obsQuery = this.useObservableQuery(); + // eslint-disable-next-line react-hooks/rules-of-hooks const result = useSyncExternalStore( + // eslint-disable-next-line react-hooks/rules-of-hooks React.useCallback( (handleStoreChange) => { if (this.renderPromises) { @@ -251,7 +310,9 @@ class InternalState { // effectively passing this dependency array to that useEffect buried // inside useSyncExternalStore, as desired. obsQuery, + // eslint-disable-next-line react-hooks/exhaustive-deps this.renderPromises, + // eslint-disable-next-line react-hooks/exhaustive-deps this.client.disableNetworkFetches, ] ), @@ -271,8 +332,8 @@ class InternalState { // useQuery method, so we can safely use these members in other/later methods // without worrying they might be uninitialized. private renderPromises: ApolloContextValue["renderPromises"]; - private queryHookOptions: QueryHookOptions; - private watchQueryOptions: WatchQueryOptions; + private queryHookOptions!: QueryHookOptions; + private watchQueryOptions!: WatchQueryOptions; private useOptions(options: QueryHookOptions) { const watchQueryOptions = this.createWatchQueryOptions( @@ -461,8 +522,8 @@ class InternalState { private onCompleted(data: TData) {} private onError(error: ApolloError) {} - private observable: ObservableQuery; - private obsQueryFields: Omit< + private observable!: ObservableQuery; + public obsQueryFields!: Omit< ObservableQueryFields, "variables" >; @@ -477,6 +538,7 @@ class InternalState { this.observable || // Reuse this.observable if possible (and not SSR) this.client.watchQuery(this.getObsQueryOptions())); + // eslint-disable-next-line react-hooks/rules-of-hooks this.obsQueryFields = React.useMemo( () => ({ refetch: obsQuery.refetch.bind(obsQuery), @@ -552,8 +614,8 @@ class InternalState { private toApolloError( result: ApolloQueryResult ): ApolloError | undefined { - return isNonEmptyArray(result.errors) - ? new ApolloError({ graphQLErrors: result.errors }) + return isNonEmptyArray(result.errors) ? + new ApolloError({ graphQLErrors: result.errors }) : result.error; } diff --git a/src/react/hooks/useQueryRefHandlers.ts b/src/react/hooks/useQueryRefHandlers.ts new file mode 100644 index 00000000000..95036eafcf3 --- /dev/null +++ b/src/react/hooks/useQueryRefHandlers.ts @@ -0,0 +1,116 @@ +import * as React from "rehackt"; +import { + assertWrappedQueryRef, + getWrappedPromise, + unwrapQueryRef, + updateWrappedQueryRef, + wrapQueryRef, +} from "../internal/index.js"; +import type { QueryRef } from "../internal/index.js"; +import type { OperationVariables } from "../../core/types.js"; +import type { RefetchFunction, FetchMoreFunction } from "./useSuspenseQuery.js"; +import type { FetchMoreQueryOptions } from "../../core/watchQueryOptions.js"; +import { useApolloClient } from "./useApolloClient.js"; +import { wrapHook } from "./internal/index.js"; + +export interface UseQueryRefHandlersResult< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +> { + /** {@inheritDoc @apollo/client!ObservableQuery#refetch:member(1)} */ + refetch: RefetchFunction; + /** {@inheritDoc @apollo/client!ObservableQuery#fetchMore:member(1)} */ + fetchMore: FetchMoreFunction; +} + +/** + * A React hook that returns a `refetch` and `fetchMore` function for a given + * `queryRef`. + * + * This is useful to get access to handlers for a `queryRef` that was created by + * `createQueryPreloader` or when the handlers for a `queryRef` produced in + * a different component are inaccessible. + * + * @example + * ```tsx + * const MyComponent({ queryRef }) { + * const { refetch, fetchMore } = useQueryRefHandlers(queryRef); + * + * // ... + * } + * ``` + * @since 3.9.0 + * @param queryRef - A `QueryRef` returned from `useBackgroundQuery`, `useLoadableQuery`, or `createQueryPreloader`. + */ +export function useQueryRefHandlers< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + queryRef: QueryRef +): UseQueryRefHandlersResult { + const unwrapped = unwrapQueryRef(queryRef); + + return wrapHook( + "useQueryRefHandlers", + _useQueryRefHandlers, + unwrapped ? + unwrapped["observable"] + // in the case of a "transported" queryRef object, we need to use the + // client that's available to us at the current position in the React tree + // that ApolloClient will then have the job to recreate a real queryRef from + // the transported object + // This is just a context read - it's fine to do this conditionally. + // This hook wrapper also shouldn't be optimized by React Compiler. + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/rules-of-hooks + : useApolloClient() + )(queryRef); +} + +function _useQueryRefHandlers< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + queryRef: QueryRef +): UseQueryRefHandlersResult { + assertWrappedQueryRef(queryRef); + const [previousQueryRef, setPreviousQueryRef] = React.useState(queryRef); + const [wrappedQueryRef, setWrappedQueryRef] = React.useState(queryRef); + const internalQueryRef = unwrapQueryRef(queryRef); + + // To ensure we can support React transitions, this hook needs to manage the + // queryRef state and apply React's state value immediately to the existing + // queryRef since this hook doesn't return the queryRef directly + if (previousQueryRef !== queryRef) { + setPreviousQueryRef(queryRef); + setWrappedQueryRef(queryRef); + } else { + updateWrappedQueryRef(queryRef, getWrappedPromise(wrappedQueryRef)); + } + + const refetch: RefetchFunction = React.useCallback( + (variables) => { + const promise = internalQueryRef.refetch(variables); + + setWrappedQueryRef(wrapQueryRef(internalQueryRef)); + + return promise; + }, + [internalQueryRef] + ); + + const fetchMore: FetchMoreFunction = React.useCallback( + (options) => { + const promise = internalQueryRef.fetchMore( + options as FetchMoreQueryOptions + ); + + setWrappedQueryRef(wrapQueryRef(internalQueryRef)); + + return promise; + }, + [internalQueryRef] + ); + + return { refetch, fetchMore }; +} diff --git a/src/react/hooks/useReactiveVar.ts b/src/react/hooks/useReactiveVar.ts index 2d14c12cd63..ed7ac2379d3 100644 --- a/src/react/hooks/useReactiveVar.ts +++ b/src/react/hooks/useReactiveVar.ts @@ -1,7 +1,24 @@ -import * as React from "react"; +import * as React from "rehackt"; import type { ReactiveVar } from "../../core/index.js"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; +/** + * Reads the value of a [reactive variable](https://www.apollographql.com/docs/react/local-state/reactive-variables/) and re-renders the containing component whenever that variable's value changes. This enables a reactive variable to trigger changes _without_ relying on the `useQuery` hook. + * + * @example + * ```jsx + * import { makeVar, useReactiveVar } from "@apollo/client"; + * export const cartItemsVar = makeVar([]); + * + * export function Cart() { + * const cartItems = useReactiveVar(cartItemsVar); + * // ... + * } + * ``` + * @since 3.2.0 + * @param rv - A reactive variable. + * @returns The current value of the reactive variable. + */ export function useReactiveVar(rv: ReactiveVar): T { return useSyncExternalStore( React.useCallback( diff --git a/src/react/hooks/useReadQuery.ts b/src/react/hooks/useReadQuery.ts index e6a97e1446f..3d6ae811df6 100644 --- a/src/react/hooks/useReadQuery.ts +++ b/src/react/hooks/useReadQuery.ts @@ -1,12 +1,17 @@ -import * as React from "react"; -import { unwrapQueryRef } from "../cache/QueryReference.js"; -import type { QueryReference } from "../cache/QueryReference.js"; -import { __use } from "./internal/index.js"; +import * as React from "rehackt"; +import { + assertWrappedQueryRef, + getWrappedPromise, + unwrapQueryRef, + updateWrappedQueryRef, +} from "../internal/index.js"; +import type { QueryRef } from "../internal/index.js"; +import { __use, wrapHook } from "./internal/index.js"; import { toApolloError } from "./useSuspenseQuery.js"; -import { invariant } from "../../utilities/globals/index.js"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; import type { ApolloError } from "../../errors/index.js"; import type { NetworkStatus } from "../../core/index.js"; +import { useApolloClient } from "./useApolloClient.js"; export interface UseReadQueryResult { /** @@ -34,34 +39,60 @@ export interface UseReadQueryResult { } export function useReadQuery( - queryRef: QueryReference + queryRef: QueryRef ): UseReadQueryResult { - const internalQueryRef = unwrapQueryRef(queryRef); - invariant( - internalQueryRef.promiseCache, - "It appears that `useReadQuery` was used outside of `useBackgroundQuery`. " + - "`useReadQuery` is only supported for use with `useBackgroundQuery`. " + - "Please ensure you are passing the `queryRef` returned from `useBackgroundQuery`." + const unwrapped = unwrapQueryRef(queryRef); + + return wrapHook( + "useReadQuery", + _useReadQuery, + unwrapped ? + unwrapped["observable"] + // in the case of a "transported" queryRef object, we need to use the + // client that's available to us at the current position in the React tree + // that ApolloClient will then have the job to recreate a real queryRef from + // the transported object + // This is just a context read - it's fine to do this conditionally. + // This hook wrapper also shouldn't be optimized by React Compiler. + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/rules-of-hooks + : useApolloClient() + )(queryRef); +} + +function _useReadQuery( + queryRef: QueryRef +): UseReadQueryResult { + assertWrappedQueryRef(queryRef); + const internalQueryRef = React.useMemo( + () => unwrapQueryRef(queryRef), + [queryRef] ); - const { promiseCache, key } = internalQueryRef; + const getPromise = React.useCallback( + () => getWrappedPromise(queryRef), + [queryRef] + ); - if (!promiseCache.has(key)) { - promiseCache.set(key, internalQueryRef.promise); + if (internalQueryRef.disposed) { + internalQueryRef.reinitialize(); + updateWrappedQueryRef(queryRef, internalQueryRef.promise); } + React.useEffect(() => internalQueryRef.retain(), [internalQueryRef]); + const promise = useSyncExternalStore( React.useCallback( (forceUpdate) => { return internalQueryRef.listen((promise) => { - internalQueryRef.promiseCache!.set(internalQueryRef.key, promise); + updateWrappedQueryRef(queryRef, promise); forceUpdate(); }); }, - [internalQueryRef] + [internalQueryRef, queryRef] ), - () => promiseCache.get(key)!, - () => promiseCache.get(key)! + getPromise, + getPromise ); const result = __use(promise); diff --git a/src/react/hooks/useSubscription.ts b/src/react/hooks/useSubscription.ts index 764879810da..0ba57c64346 100644 --- a/src/react/hooks/useSubscription.ts +++ b/src/react/hooks/useSubscription.ts @@ -1,5 +1,5 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; import type { DocumentNode } from "graphql"; import type { TypedDocumentNode } from "@graphql-typed-document-node/core"; import { equal } from "@wry/equality"; @@ -12,7 +12,91 @@ import type { } from "../types/types.js"; import type { OperationVariables } from "../../core/index.js"; import { useApolloClient } from "./useApolloClient.js"; - +/** + * > Refer to the [Subscriptions](https://www.apollographql.com/docs/react/data/subscriptions/) section for a more in-depth overview of `useSubscription`. + * + * @example + * ```jsx + * const COMMENTS_SUBSCRIPTION = gql` + * subscription OnCommentAdded($repoFullName: String!) { + * commentAdded(repoFullName: $repoFullName) { + * id + * content + * } + * } + * `; + * + * function DontReadTheComments({ repoFullName }) { + * const { + * data: { commentAdded }, + * loading, + * } = useSubscription(COMMENTS_SUBSCRIPTION, { variables: { repoFullName } }); + * return

New comment: {!loading && commentAdded.content}

; + * } + * ``` + * @remarks + * #### Subscriptions and React 18 Automatic Batching + * + * With React 18's [automatic batching](https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching), multiple state updates may be grouped into a single re-render for better performance. + * + * If your subscription API sends multiple messages at the same time or in very fast succession (within fractions of a millisecond), it is likely that only the last message received in that narrow time frame will result in a re-render. + * + * Consider the following component: + * + * ```jsx + * export function Subscriptions() { + * const { data, error, loading } = useSubscription(query); + * const [accumulatedData, setAccumulatedData] = useState([]); + * + * useEffect(() => { + * setAccumulatedData((prev) => [...prev, data]); + * }, [data]); + * + * return ( + * <> + * {loading &&

Loading...

} + * {JSON.stringify(accumulatedData, undefined, 2)} + * + * ); + * } + * ``` + * + * If your subscription back-end emits two messages with the same timestamp, only the last message received by Apollo Client will be rendered. This is because React 18 will batch these two state updates into a single re-render. + * + * Since the component above is using `useEffect` to push `data` into a piece of local state on each `Subscriptions` re-render, the first message will never be added to the `accumulatedData` array since its render was skipped. + * + * Instead of using `useEffect` here, we can re-write this component to use the `onData` callback function accepted in `useSubscription`'s `options` object: + * + * ```jsx + * export function Subscriptions() { + * const [accumulatedData, setAccumulatedData] = useState([]); + * const { data, error, loading } = useSubscription( + * query, + * { + * onData({ data }) { + * setAccumulatedData((prev) => [...prev, data]) + * } + * } + * ); + * + * return ( + * <> + * {loading &&

Loading...

} + * {JSON.stringify(accumulatedData, undefined, 2)} + * + * ); + * } + * ``` + * + * > ⚠️ **Note:** The `useSubscription` option `onData` is available in Apollo Client >= 3.7. In previous versions, the equivalent option is named `onSubscriptionData`. + * + * Now, the first message will be added to the `accumulatedData` array since `onData` is called _before_ the component re-renders. React 18 automatic batching is still in effect and results in a single re-render, but with `onData` we can guarantee each message received after the component mounts is added to `accumulatedData`. + * + * @since 3.0.0 + * @param subscription - A GraphQL subscription document parsed into an AST by `gql`. + * @param options - Options to control how the subscription is executed. + * @returns Query result object + */ export function useSubscription< TData = any, TVariables extends OperationVariables = OperationVariables, @@ -37,17 +121,17 @@ export function useSubscription< if (options?.onSubscriptionData) { invariant.warn( - options.onData - ? "'useSubscription' supports only the 'onSubscriptionData' or 'onData' option, but not both. Only the 'onData' option will be used." - : "'onSubscriptionData' is deprecated and will be removed in a future major version. Please use the 'onData' option instead." + options.onData ? + "'useSubscription' supports only the 'onSubscriptionData' or 'onData' option, but not both. Only the 'onData' option will be used." + : "'onSubscriptionData' is deprecated and will be removed in a future major version. Please use the 'onData' option instead." ); } if (options?.onSubscriptionComplete) { invariant.warn( - options.onComplete - ? "'useSubscription' supports only the 'onSubscriptionComplete' or 'onComplete' option, but not both. Only the 'onComplete' option will be used." - : "'onSubscriptionComplete' is deprecated and will be removed in a future major version. Please use the 'onComplete' option instead." + options.onComplete ? + "'useSubscription' supports only the 'onSubscriptionComplete' or 'onComplete' option, but not both. Only the 'onComplete' option will be used." + : "'onSubscriptionComplete' is deprecated and will be removed in a future major version. Please use the 'onComplete' option instead." ); } } @@ -120,6 +204,8 @@ export function useSubscription< } Object.assign(ref.current, { client, subscription, options }); + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps }, [client, subscription, options, canResetObservableRef.current]); React.useEffect(() => { @@ -187,6 +273,8 @@ export function useSubscription< subscription.unsubscribe(); }); }; + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/exhaustive-deps }, [observable]); return result; diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts index 1139b3a4984..fe438ab6240 100644 --- a/src/react/hooks/useSuspenseQuery.ts +++ b/src/react/hooks/useSuspenseQuery.ts @@ -1,4 +1,4 @@ -import * as React from "react"; +import * as React from "rehackt"; import { invariant } from "../../utilities/globals/index.js"; import type { ApolloClient, @@ -20,12 +20,12 @@ import type { ObservableQueryFields, NoInfer, } from "../types/types.js"; -import { __use, useDeepMemo } from "./internal/index.js"; -import { getSuspenseCache } from "../cache/index.js"; +import { __use, useDeepMemo, wrapHook } from "./internal/index.js"; +import { getSuspenseCache } from "../internal/index.js"; import { canonicalStringify } from "../../cache/index.js"; import { skipToken } from "./constants.js"; import type { SkipToken } from "./constants.js"; -import type { CacheKey } from "../cache/types.js"; +import type { CacheKey, QueryKey } from "../internal/index.js"; export interface UseSuspenseQueryResult< TData = unknown, @@ -71,17 +71,16 @@ export function useSuspenseQuery< options?: SuspenseQueryHookOptions, NoInfer> & TOptions ): UseSuspenseQueryResult< - TOptions["errorPolicy"] extends "ignore" | "all" - ? TOptions["returnPartialData"] extends true - ? DeepPartial | undefined - : TData | undefined - : TOptions["returnPartialData"] extends true - ? TOptions["skip"] extends boolean - ? DeepPartial | undefined - : DeepPartial - : TOptions["skip"] extends boolean - ? TData | undefined - : TData, + TOptions["errorPolicy"] extends "ignore" | "all" ? + TOptions["returnPartialData"] extends true ? + DeepPartial | undefined + : TData | undefined + : TOptions["returnPartialData"] extends true ? + TOptions["skip"] extends boolean ? + DeepPartial | undefined + : DeepPartial + : TOptions["skip"] extends boolean ? TData | undefined + : TData, TVariables >; @@ -175,10 +174,30 @@ export function useSuspenseQuery< options: | (SkipToken & Partial>) | SuspenseQueryHookOptions = Object.create(null) +): UseSuspenseQueryResult { + return wrapHook( + "useSuspenseQuery", + _useSuspenseQuery, + useApolloClient(typeof options === "object" ? options.client : undefined) + )(query, options); +} + +function _useSuspenseQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, +>( + query: DocumentNode | TypedDocumentNode, + options: + | (SkipToken & Partial>) + | SuspenseQueryHookOptions ): UseSuspenseQueryResult { const client = useApolloClient(options.client); const suspenseCache = getSuspenseCache(client); - const watchQueryOptions = useWatchQueryOptions({ client, query, options }); + const watchQueryOptions = useWatchQueryOptions({ + client, + query, + options, + }); const { fetchPolicy, variables } = watchQueryOptions; const { queryKey = [] } = options; @@ -192,29 +211,26 @@ export function useSuspenseQuery< client.watchQuery(watchQueryOptions) ); - const [promiseCache, setPromiseCache] = React.useState( - () => new Map([[queryRef.key, queryRef.promise]]) - ); - - let promise = promiseCache.get(queryRef.key); + let [current, setPromise] = React.useState< + [QueryKey, Promise>] + >([queryRef.key, queryRef.promise]); - if (queryRef.didChangeOptions(watchQueryOptions)) { - promise = queryRef.applyOptions(watchQueryOptions); - promiseCache.set(queryRef.key, promise); + // This saves us a re-execution of the render function when a variable changed. + if (current[0] !== queryRef.key) { + current[0] = queryRef.key; + current[1] = queryRef.promise; } + let promise = current[1]; - if (!promise) { - promise = queryRef.promise; - promiseCache.set(queryRef.key, promise); + if (queryRef.didChangeOptions(watchQueryOptions)) { + current[1] = promise = queryRef.applyOptions(watchQueryOptions); } React.useEffect(() => { const dispose = queryRef.retain(); const removeListener = queryRef.listen((promise) => { - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, promise) - ); + setPromise([queryRef.key, promise]); }); return () => { @@ -236,39 +252,39 @@ export function useSuspenseQuery< const result = fetchPolicy === "standby" ? skipResult : __use(promise); - const fetchMore: FetchMoreFunction = React.useCallback( + const fetchMore = React.useCallback< + FetchMoreFunction + >( (options) => { const promise = queryRef.fetchMore(options); - - setPromiseCache((previousPromiseCache) => - new Map(previousPromiseCache).set(queryRef.key, queryRef.promise) - ); + setPromise([queryRef.key, queryRef.promise]); return promise; }, [queryRef] - ); + ) as FetchMoreFunction; const refetch: RefetchFunction = React.useCallback( (variables) => { const promise = queryRef.refetch(variables); - - setPromiseCache((previousPromiseCache) => - new Map(previousPromiseCache).set(queryRef.key, queryRef.promise) - ); + setPromise([queryRef.key, queryRef.promise]); return promise; }, [queryRef] ); - const subscribeToMore: SubscribeToMoreFunction = - React.useCallback( - (options) => queryRef.observable.subscribeToMore(options), - [queryRef] - ); + const subscribeToMore: SubscribeToMoreFunction< + TData | undefined, + TVariables + > = React.useCallback( + (options) => queryRef.observable.subscribeToMore(options), + [queryRef] + ); - return React.useMemo(() => { + return React.useMemo< + UseSuspenseQueryResult + >(() => { return { client, data: result.data, @@ -318,8 +334,8 @@ function validatePartialDataReturn( } export function toApolloError(result: ApolloQueryResult) { - return isNonEmptyArray(result.errors) - ? new ApolloError({ graphQLErrors: result.errors }) + return isNonEmptyArray(result.errors) ? + new ApolloError({ graphQLErrors: result.errors }) : result.error; } diff --git a/src/react/hooks/useSyncExternalStore.ts b/src/react/hooks/useSyncExternalStore.ts index 0ae0a84d793..1cecbe90eac 100644 --- a/src/react/hooks/useSyncExternalStore.ts +++ b/src/react/hooks/useSyncExternalStore.ts @@ -1,5 +1,5 @@ import { invariant } from "../../utilities/globals/index.js"; -import * as React from "react"; +import * as React from "rehackt"; import { canUseLayoutEffect } from "../../utilities/index.js"; @@ -81,6 +81,8 @@ export const useSyncExternalStore: RealUseSESHookType = // Force a re-render. forceUpdate({ inst }); } + // React Hook React.useLayoutEffect has a missing dependency: 'inst'. Either include it or remove the dependency array. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [subscribe, value, getSnapshot]); } else { Object.assign(inst, { value, getSnapshot }); @@ -108,6 +110,8 @@ export const useSyncExternalStore: RealUseSESHookType = forceUpdate({ inst }); } }); + // React Hook React.useEffect has a missing dependency: 'inst'. Either include it or remove the dependency array. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [subscribe]); return value; diff --git a/src/react/index.ts b/src/react/index.ts index 784046d950b..13f1103e41f 100644 --- a/src/react/index.ts +++ b/src/react/index.ts @@ -13,4 +13,11 @@ export * from "./hooks/index.js"; export type { IDocumentDefinition } from "./parser/index.js"; export { DocumentType, operationName, parser } from "./parser/index.js"; +export type { + PreloadQueryOptions, + PreloadQueryFetchPolicy, + PreloadQueryFunction, +} from "./query-preloader/createQueryPreloader.js"; +export { createQueryPreloader } from "./query-preloader/createQueryPreloader.js"; + export * from "./types/types.js"; diff --git a/src/react/internal/cache/QueryReference.ts b/src/react/internal/cache/QueryReference.ts new file mode 100644 index 00000000000..b6279efd24c --- /dev/null +++ b/src/react/internal/cache/QueryReference.ts @@ -0,0 +1,529 @@ +import { equal } from "@wry/equality"; +import type { + ApolloError, + ApolloQueryResult, + ObservableQuery, + OperationVariables, + WatchQueryOptions, +} from "../../../core/index.js"; +import type { + ObservableSubscription, + PromiseWithState, +} from "../../../utilities/index.js"; +import { + createFulfilledPromise, + createRejectedPromise, +} from "../../../utilities/index.js"; +import type { QueryKey } from "./types.js"; +import { wrapPromiseWithState } from "../../../utilities/index.js"; +import { invariant } from "../../../utilities/globals/invariantWrappers.js"; + +type QueryRefPromise = PromiseWithState>; + +type Listener = (promise: QueryRefPromise) => void; + +type FetchMoreOptions = Parameters< + ObservableQuery["fetchMore"] +>[0]; + +const QUERY_REFERENCE_SYMBOL: unique symbol = Symbol(); +const PROMISE_SYMBOL: unique symbol = Symbol(); +declare const QUERY_REF_BRAND: unique symbol; +/** + * A `QueryReference` is an opaque object returned by `useBackgroundQuery`. + * A child component reading the `QueryReference` via `useReadQuery` will + * suspend until the promise resolves. + */ +export interface QueryRef { + /** @internal */ + [QUERY_REF_BRAND]?(variables: TVariables): TData; +} + +/** + * @internal + * For usage in internal helpers only. + */ +interface WrappedQueryRef + extends QueryRef { + /** @internal */ + readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; + /** @internal */ + [PROMISE_SYMBOL]: QueryRefPromise; + /** @internal */ + toPromise?(): Promise; +} + +/** + * @deprecated Please use the `QueryRef` interface instead of `QueryReference`. + * + * {@inheritDoc @apollo/client!QueryRef:interface} + */ +export interface QueryReference + extends QueryRef { + /** + * @deprecated Please use the `QueryRef` interface instead of `QueryReference`. + * + * {@inheritDoc @apollo/client!PreloadedQueryRef#toPromise:member(1)} + */ + toPromise?: unknown; +} + +/** + * {@inheritDoc @apollo/client!QueryRef:interface} + */ +export interface PreloadedQueryRef + extends QueryRef { + /** + * A function that returns a promise that resolves when the query has finished + * loading. The promise resolves with the `QueryReference` itself. + * + * @remarks + * This method is useful for preloading queries in data loading routers, such + * as [React Router](https://reactrouter.com/en/main) or [TanStack Router](https://tanstack.com/router), + * to prevent routes from transitioning until the query has finished loading. + * `data` is not exposed on the promise to discourage using the data in + * `loader` functions and exposing it to your route components. Instead, we + * prefer you rely on `useReadQuery` to access the data to ensure your + * component can rerender with cache updates. If you need to access raw query + * data, use `client.query()` directly. + * + * @example + * Here's an example using React Router's `loader` function: + * ```ts + * import { createQueryPreloader } from "@apollo/client"; + * + * const preloadQuery = createQueryPreloader(client); + * + * export async function loader() { + * const queryRef = preloadQuery(GET_DOGS_QUERY); + * + * return queryRef.toPromise(); + * } + * + * export function RouteComponent() { + * const queryRef = useLoaderData(); + * const { data } = useReadQuery(queryRef); + * + * // ... + * } + * ``` + * + * @since 3.9.0 + */ + toPromise(): Promise>; +} + +interface InternalQueryReferenceOptions { + onDispose?: () => void; + autoDisposeTimeoutMs?: number; +} + +export function wrapQueryRef( + internalQueryRef: InternalQueryReference +) { + const ref: WrappedQueryRef = { + toPromise() { + // We avoid resolving this promise with the query data because we want to + // discourage using the server data directly from the queryRef. Instead, + // the data should be accessed through `useReadQuery`. When the server + // data is needed, its better to use `client.query()` directly. + // + // Here we resolve with the ref itself to make using this in React Router + // or TanStack Router `loader` functions a bit more ergonomic e.g. + // + // function loader() { + // return { queryRef: await preloadQuery(query).toPromise() } + // } + return getWrappedPromise(ref).then(() => ref); + }, + [QUERY_REFERENCE_SYMBOL]: internalQueryRef, + [PROMISE_SYMBOL]: internalQueryRef.promise, + }; + + return ref; +} + +export function assertWrappedQueryRef( + queryRef: QueryRef +): asserts queryRef is WrappedQueryRef; +export function assertWrappedQueryRef( + queryRef: QueryRef | undefined | null +): asserts queryRef is WrappedQueryRef | undefined | null; +export function assertWrappedQueryRef( + queryRef: QueryRef | undefined | null +) { + invariant( + !queryRef || QUERY_REFERENCE_SYMBOL in queryRef, + "Expected a QueryRef object, but got something else instead." + ); +} + +export function getWrappedPromise( + queryRef: WrappedQueryRef +) { + const internalQueryRef = unwrapQueryRef(queryRef); + + return internalQueryRef.promise.status === "fulfilled" ? + internalQueryRef.promise + : queryRef[PROMISE_SYMBOL]; +} + +export function unwrapQueryRef( + queryRef: WrappedQueryRef +): InternalQueryReference; +export function unwrapQueryRef( + queryRef: Partial> +): undefined | InternalQueryReference; +export function unwrapQueryRef( + queryRef: Partial> +) { + return queryRef[QUERY_REFERENCE_SYMBOL]; +} + +export function updateWrappedQueryRef( + queryRef: WrappedQueryRef, + promise: QueryRefPromise +) { + queryRef[PROMISE_SYMBOL] = promise; +} + +const OBSERVED_CHANGED_OPTIONS = [ + "canonizeResults", + "context", + "errorPolicy", + "fetchPolicy", + "refetchWritePolicy", + "returnPartialData", +] as const; + +type ObservedOptions = Pick< + WatchQueryOptions, + (typeof OBSERVED_CHANGED_OPTIONS)[number] +>; + +export class InternalQueryReference { + public result!: ApolloQueryResult; + public readonly key: QueryKey = {}; + public readonly observable: ObservableQuery; + + public promise!: QueryRefPromise; + + private subscription!: ObservableSubscription; + private listeners = new Set>(); + private autoDisposeTimeoutId?: NodeJS.Timeout; + + private resolve: ((result: ApolloQueryResult) => void) | undefined; + private reject: ((error: unknown) => void) | undefined; + + private references = 0; + private softReferences = 0; + + constructor( + observable: ObservableQuery, + options: InternalQueryReferenceOptions + ) { + this.handleNext = this.handleNext.bind(this); + this.handleError = this.handleError.bind(this); + this.dispose = this.dispose.bind(this); + this.observable = observable; + + if (options.onDispose) { + this.onDispose = options.onDispose; + } + + this.setResult(); + this.subscribeToQuery(); + + // Start a timer that will automatically dispose of the query if the + // suspended resource does not use this queryRef in the given time. This + // helps prevent memory leaks when a component has unmounted before the + // query has finished loading. + const startDisposeTimer = () => { + if (!this.references) { + this.autoDisposeTimeoutId = setTimeout( + this.dispose, + options.autoDisposeTimeoutMs ?? 30_000 + ); + } + }; + + // We wait until the request has settled to ensure we don't dispose of the + // query ref before the request finishes, otherwise we would leave the + // promise in a pending state rendering the suspense boundary indefinitely. + this.promise.then(startDisposeTimer, startDisposeTimer); + } + + get disposed() { + return this.subscription.closed; + } + + get watchQueryOptions() { + return this.observable.options; + } + + reinitialize() { + const { observable } = this; + + const originalFetchPolicy = this.watchQueryOptions.fetchPolicy; + const avoidNetworkRequests = + originalFetchPolicy === "no-cache" || originalFetchPolicy === "standby"; + + try { + if (avoidNetworkRequests) { + observable.silentSetOptions({ fetchPolicy: "standby" }); + } else { + observable.resetLastResults(); + observable.silentSetOptions({ fetchPolicy: "cache-first" }); + } + + this.subscribeToQuery(); + + if (avoidNetworkRequests) { + return; + } + + observable.resetDiff(); + this.setResult(); + } finally { + observable.silentSetOptions({ fetchPolicy: originalFetchPolicy }); + } + } + + retain() { + this.references++; + clearTimeout(this.autoDisposeTimeoutId); + let disposed = false; + + return () => { + if (disposed) { + return; + } + + disposed = true; + this.references--; + + setTimeout(() => { + if (!this.references) { + this.dispose(); + } + }); + }; + } + + softRetain() { + this.softReferences++; + let disposed = false; + + return () => { + // Tracking if this has already been called helps ensure that + // multiple calls to this function won't decrement the reference + // counter more than it should. Subsequent calls just result in a noop. + if (disposed) { + return; + } + + disposed = true; + this.softReferences--; + setTimeout(() => { + if (!this.softReferences && !this.references) { + this.dispose(); + } + }); + }; + } + + didChangeOptions(watchQueryOptions: ObservedOptions) { + return OBSERVED_CHANGED_OPTIONS.some( + (option) => + option in watchQueryOptions && + !equal(this.watchQueryOptions[option], watchQueryOptions[option]) + ); + } + + applyOptions(watchQueryOptions: ObservedOptions) { + const { + fetchPolicy: currentFetchPolicy, + canonizeResults: currentCanonizeResults, + } = this.watchQueryOptions; + + // "standby" is used when `skip` is set to `true`. Detect when we've + // enabled the query (i.e. `skip` is `false`) to execute a network request. + if ( + currentFetchPolicy === "standby" && + currentFetchPolicy !== watchQueryOptions.fetchPolicy + ) { + this.initiateFetch(this.observable.reobserve(watchQueryOptions)); + } else { + this.observable.silentSetOptions(watchQueryOptions); + + if (currentCanonizeResults !== watchQueryOptions.canonizeResults) { + this.result = { ...this.result, ...this.observable.getCurrentResult() }; + this.promise = createFulfilledPromise(this.result); + } + } + + return this.promise; + } + + listen(listener: Listener) { + this.listeners.add(listener); + + return () => { + this.listeners.delete(listener); + }; + } + + refetch(variables: OperationVariables | undefined) { + return this.initiateFetch(this.observable.refetch(variables)); + } + + fetchMore(options: FetchMoreOptions) { + return this.initiateFetch(this.observable.fetchMore(options)); + } + + private dispose() { + this.subscription.unsubscribe(); + this.onDispose(); + } + + private onDispose() { + // noop. overridable by options + } + + private handleNext(result: ApolloQueryResult) { + switch (this.promise.status) { + case "pending": { + // Maintain the last successful `data` value if the next result does not + // have one. + if (result.data === void 0) { + result.data = this.result.data; + } + this.result = result; + this.resolve?.(result); + break; + } + default: { + // This occurs when switching to a result that is fully cached when this + // class is instantiated. ObservableQuery will run reobserve when + // subscribing, which delivers a result from the cache. + if ( + result.data === this.result.data && + result.networkStatus === this.result.networkStatus + ) { + return; + } + + // Maintain the last successful `data` value if the next result does not + // have one. + if (result.data === void 0) { + result.data = this.result.data; + } + + this.result = result; + this.promise = createFulfilledPromise(result); + this.deliver(this.promise); + break; + } + } + } + + private handleError(error: ApolloError) { + this.subscription.unsubscribe(); + this.subscription = this.observable.resubscribeAfterError( + this.handleNext, + this.handleError + ); + + switch (this.promise.status) { + case "pending": { + this.reject?.(error); + break; + } + default: { + this.promise = createRejectedPromise>(error); + this.deliver(this.promise); + } + } + } + + private deliver(promise: QueryRefPromise) { + this.listeners.forEach((listener) => listener(promise)); + } + + private initiateFetch(returnedPromise: Promise>) { + this.promise = this.createPendingPromise(); + this.promise.catch(() => {}); + + // If the data returned from the fetch is deeply equal to the data already + // in the cache, `handleNext` will not be triggered leaving the promise we + // created in a pending state forever. To avoid this situtation, we attempt + // to resolve the promise if `handleNext` hasn't been run to ensure the + // promise is resolved correctly. + returnedPromise + .then(() => { + // In the case of `fetchMore`, this promise is resolved before a cache + // result is emitted due to the fact that `fetchMore` sets a `no-cache` + // fetch policy and runs `cache.batch` in its `.then` handler. Because + // the timing is different, we accidentally run this update twice + // causing an additional re-render with the `fetchMore` result by + // itself. By wrapping in `setTimeout`, this should provide a short + // delay to allow the `QueryInfo.notify` handler to run before this + // promise is checked. + // See https://github.com/apollographql/apollo-client/issues/11315 for + // more information + setTimeout(() => { + if (this.promise.status === "pending") { + // Use the current result from the observable instead of the value + // resolved from the promise. This avoids issues in some cases where + // the raw resolved value should not be the emitted value, such as + // when a `fetchMore` call returns an empty array after it has + // reached the end of the list. + // + // See the following for more information: + // https://github.com/apollographql/apollo-client/issues/11642 + this.result = this.observable.getCurrentResult(); + this.resolve?.(this.result); + } + }); + }) + .catch(() => {}); + + return returnedPromise; + } + + private subscribeToQuery() { + this.subscription = this.observable + .filter( + (result) => !equal(result.data, {}) && !equal(result, this.result) + ) + .subscribe(this.handleNext, this.handleError); + } + + private setResult() { + // Don't save this result as last result to prevent delivery of last result + // when first subscribing + const result = this.observable.getCurrentResult(false); + + if (equal(result, this.result)) { + return; + } + + this.result = result; + this.promise = + ( + result.data && + (!result.partial || this.watchQueryOptions.returnPartialData) + ) ? + createFulfilledPromise(result) + : this.createPendingPromise(); + } + + private createPendingPromise() { + return wrapPromiseWithState( + new Promise>((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }) + ); + } +} diff --git a/src/react/cache/SuspenseCache.ts b/src/react/internal/cache/SuspenseCache.ts similarity index 79% rename from src/react/cache/SuspenseCache.ts rename to src/react/internal/cache/SuspenseCache.ts index e5f8036ecea..8b1eba321b5 100644 --- a/src/react/cache/SuspenseCache.ts +++ b/src/react/internal/cache/SuspenseCache.ts @@ -1,6 +1,6 @@ import { Trie } from "@wry/trie"; -import type { ObservableQuery } from "../../core/index.js"; -import { canUseWeakMap } from "../../utilities/index.js"; +import type { ObservableQuery } from "../../../core/index.js"; +import { canUseWeakMap } from "../../../utilities/index.js"; import { InternalQueryReference } from "./QueryReference.js"; import type { CacheKey } from "./types.js"; @@ -32,11 +32,12 @@ export class SuspenseCache { cacheKey: CacheKey, createObservable: () => ObservableQuery ) { - const ref = this.queryRefs.lookupArray(cacheKey); + const ref = this.queryRefs.lookupArray(cacheKey) as { + current?: InternalQueryReference; + }; if (!ref.current) { ref.current = new InternalQueryReference(createObservable(), { - key: cacheKey, autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs, onDispose: () => { delete ref.current; @@ -44,6 +45,11 @@ export class SuspenseCache { }); } - return ref.current as InternalQueryReference; + return ref.current; + } + + add(cacheKey: CacheKey, queryRef: InternalQueryReference) { + const ref = this.queryRefs.lookupArray(cacheKey); + ref.current = queryRef; } } diff --git a/src/react/internal/cache/__tests__/QueryReference.test.tsx b/src/react/internal/cache/__tests__/QueryReference.test.tsx new file mode 100644 index 00000000000..13cf85b3586 --- /dev/null +++ b/src/react/internal/cache/__tests__/QueryReference.test.tsx @@ -0,0 +1,281 @@ +import { + ApolloClient, + ApolloLink, + InMemoryCache, + Observable, +} from "../../../../core"; +import { setupSimpleCase } from "../../../../testing/internal"; +import { + InternalQueryReference, + PreloadedQueryRef, + QueryRef, + QueryReference, +} from "../QueryReference"; +import React from "react"; + +test("kicks off request immediately when created", async () => { + const { query } = setupSimpleCase(); + let fetchCount = 0; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink((operation) => { + fetchCount++; + return Observable.of({ data: { greeting: "Hello" } }); + }), + }); + + const observable = client.watchQuery({ query }); + + expect(fetchCount).toBe(0); + new InternalQueryReference(observable, {}); + expect(fetchCount).toBe(1); +}); + +test.skip("type tests", () => { + test("passing as prop", () => { + const ANY: any = {}; + + interface Data { + foo: string; + } + interface Vars { + bar: string; + } + function ComponentWithQueryRefProp< + TData = unknown, + TVariables = unknown, + >({}: { queryRef: QueryRef }) { + return null; + } + function ComponentWithQueryReferenceProp< + TData = unknown, + TVariables = unknown, + >({}: { queryRef: QueryReference }) { + return null; + } + function ComponentWithPreloadedQueryRefProp< + TData = unknown, + TVariables = unknown, + >({}: { queryRef: PreloadedQueryRef }) { + return null; + } + + { + const withoutTypes: QueryRef = ANY; + const withData: QueryRef = ANY; + const withDataAndVariables: QueryRef = ANY; + + <> + {/* passing queryRef into components that expect queryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + queryRef={withDataAndVariables} /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing queryRef into components that expect queryReference */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing queryRef into components that expect preloadedQueryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + /* @ts-expect-error */ + queryRef={withData} + /> + /* @ts-expect-error */ + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + /* @ts-expect-error */ + queryRef={withData} + /> + /* @ts-expect-error */ + queryRef={withDataAndVariables} + /> + + ; + } + { + const withoutTypes: QueryReference = ANY; + const withData: QueryReference = ANY; + const withDataAndVariables: QueryReference = ANY; + <> + {/* passing queryReference into components that expect queryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + queryRef={withDataAndVariables} /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing queryReference into components that expect queryReference */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing queryReference into components that expect preloadedQueryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + /* @ts-expect-error */ + queryRef={withData} + /> + /* @ts-expect-error */ + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + /* @ts-expect-error */ + queryRef={withData} + /> + /* @ts-expect-error */ + queryRef={withDataAndVariables} + /> + + ; + } + { + const withoutTypes: PreloadedQueryRef = ANY; + const withData: PreloadedQueryRef = ANY; + const withDataAndVariables: PreloadedQueryRef = ANY; + <> + {/* passing preloadedQueryRef into components that expect queryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + queryRef={withDataAndVariables} /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing preloadedQueryRef into components that expect queryReference */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + {/* passing preloadedQueryRef into components that expect preloadedQueryRef */} + <> + + + + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + /* @ts-expect-error */ + queryRef={withoutTypes} + /> + queryRef={withData} /> + + queryRef={withDataAndVariables} + /> + + ; + } + }); +}); diff --git a/src/react/cache/getSuspenseCache.ts b/src/react/internal/cache/getSuspenseCache.ts similarity index 75% rename from src/react/cache/getSuspenseCache.ts rename to src/react/internal/cache/getSuspenseCache.ts index 93fbc72b98a..d9547cdc995 100644 --- a/src/react/cache/getSuspenseCache.ts +++ b/src/react/internal/cache/getSuspenseCache.ts @@ -1,8 +1,8 @@ -import type { SuspenseCacheOptions } from "./index.js"; +import type { SuspenseCacheOptions } from "../index.js"; import { SuspenseCache } from "./SuspenseCache.js"; -import type { ApolloClient } from "../../core/ApolloClient.js"; +import type { ApolloClient } from "../../../core/ApolloClient.js"; -declare module "../../core/ApolloClient.js" { +declare module "../../../core/ApolloClient.js" { interface DefaultOptions { react?: { suspense?: Readonly; diff --git a/src/react/cache/types.ts b/src/react/internal/cache/types.ts similarity index 73% rename from src/react/cache/types.ts rename to src/react/internal/cache/types.ts index e9e0ba8b826..40f3c4cc8fc 100644 --- a/src/react/cache/types.ts +++ b/src/react/internal/cache/types.ts @@ -5,3 +5,7 @@ export type CacheKey = [ stringifiedVariables: string, ...queryKey: any[], ]; + +export interface QueryKey { + __queryKey?: string; +} diff --git a/src/react/internal/index.ts b/src/react/internal/index.ts new file mode 100644 index 00000000000..7768265bf9c --- /dev/null +++ b/src/react/internal/index.ts @@ -0,0 +1,17 @@ +export { getSuspenseCache } from "./cache/getSuspenseCache.js"; +export type { CacheKey, QueryKey } from "./cache/types.js"; +export type { + QueryReference, + QueryRef, + PreloadedQueryRef, +} from "./cache/QueryReference.js"; +export { + InternalQueryReference, + getWrappedPromise, + unwrapQueryRef, + updateWrappedQueryRef, + wrapQueryRef, + assertWrappedQueryRef, +} from "./cache/QueryReference.js"; +export type { SuspenseCacheOptions } from "./cache/SuspenseCache.js"; +export type { HookWrappers } from "../hooks/internal/wrapHook.js"; diff --git a/src/react/parser/index.ts b/src/react/parser/index.ts index 984b491142f..6bcf2989989 100644 --- a/src/react/parser/index.ts +++ b/src/react/parser/index.ts @@ -6,6 +6,12 @@ import type { VariableDefinitionNode, OperationDefinitionNode, } from "graphql"; +import { + AutoCleanedWeakCache, + cacheSizes, + defaultCacheSizes, +} from "../../utilities/index.js"; +import { registerGlobalCache } from "../../utilities/caching/getMemoryInternals.js"; export enum DocumentType { Query, @@ -19,7 +25,16 @@ export interface IDocumentDefinition { variables: ReadonlyArray; } -const cache = new Map(); +let cache: + | undefined + | AutoCleanedWeakCache< + DocumentNode, + { + name: string; + type: DocumentType; + variables: readonly VariableDefinitionNode[]; + } + >; export function operationName(type: DocumentType) { let name; @@ -39,6 +54,11 @@ export function operationName(type: DocumentType) { // This parser is mostly used to safety check incoming documents. export function parser(document: DocumentNode): IDocumentDefinition { + if (!cache) { + cache = new AutoCleanedWeakCache( + cacheSizes.parser || defaultCacheSizes.parser + ); + } const cached = cache.get(document); if (cached) return cached; @@ -102,10 +122,9 @@ export function parser(document: DocumentNode): IDocumentDefinition { type = queries.length ? DocumentType.Query : DocumentType.Mutation; if (!queries.length && !mutations.length) type = DocumentType.Subscription; - const definitions = queries.length - ? queries - : mutations.length - ? mutations + const definitions = + queries.length ? queries + : mutations.length ? mutations : subscriptions; invariant( @@ -131,6 +150,14 @@ export function parser(document: DocumentNode): IDocumentDefinition { return payload; } +parser.resetCache = () => { + cache = undefined; +}; + +if (__DEV__) { + registerGlobalCache("parser", () => (cache ? cache.size : 0)); +} + export function verifyDocumentType(document: DocumentNode, type: DocumentType) { const operation = parser(document); const requiredOperationName = operationName(type); diff --git a/src/react/query-preloader/__tests__/createQueryPreloader.test.tsx b/src/react/query-preloader/__tests__/createQueryPreloader.test.tsx new file mode 100644 index 00000000000..8ab59054dd6 --- /dev/null +++ b/src/react/query-preloader/__tests__/createQueryPreloader.test.tsx @@ -0,0 +1,2514 @@ +import React, { Suspense } from "react"; +import { createQueryPreloader } from "../createQueryPreloader"; +import { + ApolloClient, + ApolloError, + ApolloLink, + InMemoryCache, + NetworkStatus, + OperationVariables, + TypedDocumentNode, + gql, +} from "../../../core"; +import { + MockLink, + MockSubscriptionLink, + MockedResponse, + wait, +} from "../../../testing"; +import { expectTypeOf } from "expect-type"; +import { PreloadedQueryRef, QueryRef, unwrapQueryRef } from "../../internal"; +import { DeepPartial, Observable } from "../../../utilities"; +import { + SimpleCaseData, + createProfiler, + spyOnConsole, + setupSimpleCase, + useTrackRenders, + setupVariablesCase, + renderWithClient, + VariablesCaseData, +} from "../../../testing/internal"; +import { ApolloProvider } from "../../context"; +import { act, render, renderHook, screen } from "@testing-library/react"; +import { UseReadQueryResult, useReadQuery } from "../../hooks"; +import { GraphQLError } from "graphql"; +import { ErrorBoundary } from "react-error-boundary"; +import userEvent from "@testing-library/user-event"; + +function createDefaultClient(mocks: MockedResponse[]) { + return new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + }); +} + +function renderDefaultTestApp({ + client, + queryRef, +}: { + client: ApolloClient; + queryRef: QueryRef; +}) { + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + error: null as Error | null, + }, + }); + + function ReadQueryHook() { + useTrackRenders({ name: "ReadQueryHook" }); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + function SuspenseFallback() { + useTrackRenders({ name: "SuspenseFallback" }); + return

Loading

; + } + + function ErrorFallback({ error }: { error: Error }) { + useTrackRenders({ name: "ErrorFallback" }); + Profiler.mergeSnapshot({ error }); + + return null; + } + + function App() { + useTrackRenders({ name: "App" }); + + return ( + + }> + + + + ); + } + + const utils = render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + function rerender() { + return utils.rerender(); + } + + return { ...utils, rerender, Profiler }; +} + +test("loads a query and suspends when passed to useReadQuery", async () => { + const { query, mocks } = setupSimpleCase(); + const client = createDefaultClient(mocks); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("loads a query with variables and suspends when passed to useReadQuery", async () => { + const { query, mocks } = setupVariablesCase(); + const client = createDefaultClient(mocks); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query, { + variables: { id: "1" }, + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("Auto disposes of the query ref if not retained within the given time", async () => { + jest.useFakeTimers(); + const { query, mocks } = setupSimpleCase(); + const client = createDefaultClient(mocks); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query); + + // We don't start the dispose timer until the promise is initially resolved + // so we need to wait for it + jest.advanceTimersByTime(20); + await queryRef.toPromise(); + jest.advanceTimersByTime(30_000); + + expect(queryRef).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); + + jest.useRealTimers(); +}); + +test("Honors configured auto dispose timer on the client", async () => { + jest.useFakeTimers(); + const { query, mocks } = setupSimpleCase(); + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink(mocks), + defaultOptions: { + react: { + suspense: { + autoDisposeTimeoutMs: 5000, + }, + }, + }, + }); + + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query); + + // We don't start the dispose timer until the promise is initially resolved + // so we need to wait for it + jest.advanceTimersByTime(20); + await queryRef.toPromise(); + jest.advanceTimersByTime(5_000); + + expect(queryRef).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); + + jest.useRealTimers(); +}); + +test("useReadQuery auto-retains the queryRef and disposes of it when unmounted", async () => { + jest.useFakeTimers(); + const { query, mocks } = setupSimpleCase(); + + const client = createDefaultClient(mocks); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query); + + const { unmount } = renderHook(() => useReadQuery(queryRef)); + + // We don't start the dispose timer until the promise is initially resolved + // so we need to wait for it + jest.advanceTimersByTime(20); + await act(() => queryRef.toPromise()); + jest.advanceTimersByTime(30_000); + + expect(queryRef).not.toBeDisposed(); + + jest.useRealTimers(); + + unmount(); + + await wait(0); + + expect(queryRef).toBeDisposed(); + expect(client.getObservableQueries().size).toBe(0); + expect(client).not.toHaveSuspenseCacheEntryUsing(query); +}); + +test("useReadQuery auto-resubscribes the query after its disposed", async () => { + const { query } = setupSimpleCase(); + + let fetchCount = 0; + const link = new ApolloLink((operation) => { + let count = ++fetchCount; + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: `Hello ${count}` } }); + observer.complete(); + }, 100); + }); + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + const user = userEvent.setup(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query); + + function SuspenseFallback() { + useTrackRenders(); + return
Loading
; + } + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + + return ( + <> + + }> + {show && } + + + ); + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithClient(, { client, wrapper: Profiler }); + + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await wait(0); + await Profiler.takeRender(); + + expect(queryRef).toBeDisposed(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // Ensure we aren't refetching the data by checking we still render the same + // cache result + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + client.writeQuery({ query, data: { greeting: "Hello (cached)" } }); + + // Ensure we can get cache updates again after remounting + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello (cached)" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Write a cache result to ensure that remounting will read this result + // instead of the old one + client.writeQuery({ query, data: { greeting: "While you were away" } }); + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(queryRef).not.toBeDisposed(); + + // Ensure we read the newest cache result changed while this queryRef was + // disposed + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "While you were away" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Remove cached data to ensure remounting will refetch the data + client.cache.modify({ + fields: { + greeting: (_, { DELETE }) => DELETE, + }, + }); + + // we wait a moment to ensure no network request is triggered + // by the `cache.modify` (even with a slight delay) + await wait(10); + expect(fetchCount).toBe(1); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // this should now trigger a network request + expect(fetchCount).toBe(2); + expect(queryRef).not.toBeDisposed(); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 2" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("useReadQuery handles auto-resubscribe with returnPartialData", async () => { + const { query, mocks } = setupVariablesCase(); + + let fetchCount = 0; + const link = new ApolloLink((operation) => { + fetchCount++; + const mock = mocks.find( + (mock) => mock.request.variables?.id === operation.variables.id + ); + + if (!mock) { + throw new Error("Could not find mock for variables"); + } + + const result = mock.result as Record; + + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: result.data }); + observer.complete(); + }, 100); + }); + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult> | null, + }, + }); + const user = userEvent.setup(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query, { + returnPartialData: true, + variables: { id: "1" }, + }); + + function SuspenseFallback() { + useTrackRenders(); + return
Loading
; + } + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + + return ( + <> + + }> + {show && } + + + ); + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithClient(, { client, wrapper: Profiler }); + + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await wait(0); + await Profiler.takeRender(); + + expect(queryRef).toBeDisposed(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // Ensure we aren't refetching the data by checking we still render the same + // cache result + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + client.writeQuery({ + query, + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (cached)", + }, + }, + variables: { id: "1" }, + }); + + // Ensure we can get cache updates again after remounting + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (cached)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Write a cache result to ensure that remounting will read this result + // instead of the old one + client.writeQuery({ + query, + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (Away)", + }, + }, + variables: { id: "1" }, + }); + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(queryRef).not.toBeDisposed(); + + // Ensure we read the newest cache result changed while this queryRef was + // disposed + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { + __typename: "Character", + id: "1", + name: "Spider-Man (Away)", + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Remove cached data to ensure remounting will refetch the data + client.cache.modify({ + id: "Character:1", + fields: { + name: (_, { DELETE }) => DELETE, + }, + }); + + // we wait a moment to ensure no network request is triggered + // by the `cache.modify` (even with a slight delay) + await wait(10); + expect(fetchCount).toBe(1); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // this should now trigger a network request + expect(fetchCount).toBe(2); + expect(queryRef).not.toBeDisposed(); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + // Ensure that remounting without data in the cache will fetch and suspend + client.clearStore(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(fetchCount).toBe(3); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("useReadQuery handles auto-resubscribe on network-only fetch policy", async () => { + const { query } = setupSimpleCase(); + + let fetchCount = 0; + const link = new ApolloLink((operation) => { + let count = ++fetchCount; + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: `Hello ${count}` } }); + observer.complete(); + }, 10); + }); + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + const user = userEvent.setup(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query, { fetchPolicy: "network-only" }); + + function SuspenseFallback() { + useTrackRenders(); + return
Loading
; + } + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + + return ( + <> + + }> + {show && } + + + ); + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithClient(, { client, wrapper: Profiler }); + + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await wait(0); + await Profiler.takeRender(); + + expect(queryRef).toBeDisposed(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // Ensure we aren't refetching the data by checking we still render the same + // cache result + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + client.writeQuery({ query, data: { greeting: "Hello (cached)" } }); + + // Ensure we can get cache updates again after remounting + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello (cached)" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Write a cache result to ensure that remounting will read this result + // instead of the old one + client.writeQuery({ query, data: { greeting: "While you were away" } }); + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(queryRef).not.toBeDisposed(); + + // Ensure we read the newest cache result changed while this queryRef was + // disposed + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "While you were away" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Remove cached data to ensure remounting will refetch the data + client.cache.modify({ + fields: { + greeting: (_, { DELETE }) => DELETE, + }, + }); + + // Ensure the delete doesn't immediately fetch + await wait(10); + expect(fetchCount).toBe(1); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(fetchCount).toBe(2); + expect(queryRef).not.toBeDisposed(); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 2" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("useReadQuery handles auto-resubscribe on cache-and-network fetch policy", async () => { + const { query } = setupSimpleCase(); + + let fetchCount = 0; + const link = new ApolloLink((operation) => { + let count = ++fetchCount; + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: `Hello ${count}` } }); + observer.complete(); + }, 10); + }); + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + const user = userEvent.setup(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query, { fetchPolicy: "cache-and-network" }); + + function SuspenseFallback() { + useTrackRenders(); + return
Loading
; + } + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + + return ( + <> + + }> + {show && } + + + ); + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithClient(, { client, wrapper: Profiler }); + + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await wait(0); + await Profiler.takeRender(); + + expect(queryRef).toBeDisposed(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // Ensure we aren't refetching the data by checking we still render the same + // cache result + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + client.writeQuery({ query, data: { greeting: "Hello (cached)" } }); + + // Ensure we can get cache updates again after remounting + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello (cached)" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Write a cache result to ensure that remounting will read this result + // instead of the old one + client.writeQuery({ query, data: { greeting: "While you were away" } }); + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(queryRef).not.toBeDisposed(); + + // Ensure we read the newest cache result changed while this queryRef was + // disposed + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "While you were away" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Remove cached data to ensure remounting will refetch the data + client.cache.modify({ + fields: { + greeting: (_, { DELETE }) => DELETE, + }, + }); + + // Ensure delete doesn't refetch immediately + await wait(10); + expect(fetchCount).toBe(1); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(fetchCount).toBe(2); + expect(queryRef).not.toBeDisposed(); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 2" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("useReadQuery handles auto-resubscribe on no-cache fetch policy", async () => { + const { query } = setupSimpleCase(); + + let fetchCount = 0; + const link = new ApolloLink((operation) => { + let count = ++fetchCount; + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: `Hello ${count}` } }); + observer.complete(); + }, 10); + }); + }); + + const Profiler = createProfiler({ + initialSnapshot: { + result: null as UseReadQueryResult | null, + }, + }); + const user = userEvent.setup(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + const preloadQuery = createQueryPreloader(client); + + const queryRef = preloadQuery(query, { fetchPolicy: "no-cache" }); + + function SuspenseFallback() { + useTrackRenders(); + return
Loading
; + } + + function App() { + useTrackRenders(); + const [show, setShow] = React.useState(true); + + return ( + <> + + }> + {show && } + + + ); + } + + function ReadQueryHook() { + useTrackRenders(); + Profiler.mergeSnapshot({ result: useReadQuery(queryRef) }); + + return null; + } + + renderWithClient(, { client, wrapper: Profiler }); + + const toggleButton = screen.getByText("Toggle"); + + // initial render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await wait(0); + await Profiler.takeRender(); + + expect(queryRef).toBeDisposed(); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + // Ensure we aren't refetching the data by checking we still render the same + // result + { + const { renderedComponents, snapshot } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + // Ensure caches writes for the query are ignored by the hook + client.writeQuery({ query, data: { greeting: "Hello (cached)" } }); + + await expect(Profiler).not.toRerender(); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Write a cache result to ensure that remounting will ignore this result + client.writeQuery({ query, data: { greeting: "While you were away" } }); + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(queryRef).not.toBeDisposed(); + + // Ensure we continue to read the same value + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(fetchCount).toBe(1); + + // unmount ReadQueryHook + await act(() => user.click(toggleButton)); + await Profiler.takeRender(); + await wait(0); + + expect(queryRef).toBeDisposed(); + + // Remove cached data to verify this type of cache change is also ignored + client.cache.modify({ + fields: { + greeting: (_, { DELETE }) => DELETE, + }, + }); + + // Ensure delete doesn't fire off request + await wait(10); + expect(fetchCount).toBe(1); + + // mount ReadQueryHook + await act(() => user.click(toggleButton)); + + expect(fetchCount).toBe(1); + expect(queryRef).not.toBeDisposed(); + + // Ensure we are still rendering the same result and haven't refetched + // anything based on missing cache data + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, ReadQueryHook]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello 1" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("reacts to cache updates", async () => { + const { query, mocks } = setupSimpleCase(); + const client = createDefaultClient(mocks); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + client.writeQuery({ + query, + data: { greeting: "Hello (updated)" }, + }); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello (updated)" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("ignores cached result and suspends when `fetchPolicy` is network-only", async () => { + const { query, mocks } = setupSimpleCase(); + + const client = createDefaultClient(mocks); + client.writeQuery({ query, data: { greeting: "Cached Hello" } }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + fetchPolicy: "network-only", + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("does not cache results when `fetchPolicy` is no-cache", async () => { + const { query, mocks } = setupSimpleCase(); + + const client = createDefaultClient(mocks); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + fetchPolicy: "no-cache", + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + expect(client.extract()).toEqual({}); +}); + +test("returns initial cache data followed by network data when `fetchPolicy` is cache-and-network", async () => { + const { query, mocks } = setupSimpleCase(); + + const client = createDefaultClient(mocks); + client.writeQuery({ query, data: { greeting: "Cached Hello" } }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + fetchPolicy: "cache-and-network", + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { greeting: "Cached Hello" }, + error: undefined, + networkStatus: NetworkStatus.loading, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { greeting: "Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +test("returns cached data when all data is present in the cache", async () => { + const { query, mocks } = setupSimpleCase(); + + const client = createDefaultClient(mocks); + client.writeQuery({ query, data: { greeting: "Cached Hello" } }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { greeting: "Cached Hello" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("suspends and ignores partial data in the cache", async () => { + const query = gql` + query { + hello + foo + } + `; + + const mocks = [ + { + request: { query }, + result: { data: { hello: "from link", foo: "bar" } }, + delay: 20, + }, + ]; + + const client = createDefaultClient(mocks); + + { + // we expect a "Missing field 'foo' while writing result..." error + // when writing hello to the cache, so we'll silence it + using _consoleSpy = spyOnConsole("error"); + client.writeQuery({ query, data: { hello: "from cache" } }); + } + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { hello: "from link", foo: "bar" }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + await expect(Profiler).not.toRerender(); +}); + +test("throws when error is returned", async () => { + // Disable error messages shown by React when an error is thrown to an error + // boundary + using _consoleSpy = spyOnConsole("error"); + const { query } = setupSimpleCase(); + const mocks = [ + { request: { query }, result: { errors: [new GraphQLError("Oops")] } }, + ]; + const client = createDefaultClient(mocks); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ErrorFallback"]); + expect(snapshot.error).toEqual( + new ApolloError({ graphQLErrors: [new GraphQLError("Oops")] }) + ); + } +}); + +test("returns error when error policy is 'all'", async () => { + // Disable error messages shown by React when an error is thrown to an error + // boundary + using _consoleSpy = spyOnConsole("error"); + const { query } = setupSimpleCase(); + const mocks = [ + { request: { query }, result: { errors: [new GraphQLError("Oops")] } }, + ]; + const client = createDefaultClient(mocks); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { errorPolicy: "all" }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: undefined, + error: new ApolloError({ graphQLErrors: [new GraphQLError("Oops")] }), + networkStatus: NetworkStatus.error, + }); + expect(snapshot.error).toEqual(null); + } +}); + +test("discards error when error policy is 'ignore'", async () => { + // Disable error messages shown by React when an error is thrown to an error + // boundary + using _consoleSpy = spyOnConsole("error"); + const { query } = setupSimpleCase(); + const mocks = [ + { request: { query }, result: { errors: [new GraphQLError("Oops")] } }, + ]; + const client = createDefaultClient(mocks); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { errorPolicy: "ignore" }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: undefined, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + expect(snapshot.error).toEqual(null); + } +}); + +test("passes context to the link", async () => { + interface QueryData { + context: Record; + } + + const query: TypedDocumentNode = gql` + query ContextQuery { + context + } + `; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new ApolloLink((operation) => { + return new Observable((observer) => { + const { valueA, valueB } = operation.getContext(); + setTimeout(() => { + observer.next({ data: { context: { valueA, valueB } } }); + observer.complete(); + }, 10); + }); + }), + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + context: { valueA: "A", valueB: "B" }, + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + // initial render + await Profiler.takeRender(); + + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result).toEqual({ + data: { context: { valueA: "A", valueB: "B" } }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); +}); + +test("creates unique query refs when calling preloadQuery with the same query", async () => { + const { query } = setupSimpleCase(); + + const mocks: MockedResponse[] = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + maxUsageCount: Infinity, + }, + ]; + + const client = createDefaultClient(mocks); + const preloadQuery = createQueryPreloader(client); + + const queryRef1 = preloadQuery(query); + const queryRef2 = preloadQuery(query); + + const unwrappedQueryRef1 = unwrapQueryRef(queryRef1); + const unwrappedQueryRef2 = unwrapQueryRef(queryRef2); + + // Use Object.is inside expect to prevent circular reference errors on toBe + expect(Object.is(queryRef1, queryRef2)).toBe(false); + expect(Object.is(unwrappedQueryRef1, unwrappedQueryRef2)).toBe(false); + + await expect(queryRef1.toPromise()).resolves.toBe(queryRef1); + await expect(queryRef2.toPromise()).resolves.toBe(queryRef2); +}); + +test("does not suspend and returns partial data when `returnPartialData` is `true`", async () => { + const { query, mocks } = setupVariablesCase(); + const partialQuery = gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + } + } + `; + + const client = createDefaultClient(mocks); + + client.writeQuery({ + query: partialQuery, + data: { character: { __typename: "Character", id: "1" } }, + variables: { id: "1" }, + }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { + variables: { id: "1" }, + returnPartialData: true, + }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { character: { __typename: "Character", id: "1" } }, + networkStatus: NetworkStatus.loading, + error: undefined, + }); + } + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { + character: { __typename: "Character", id: "1", name: "Spider-Man" }, + }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + } +}); + +test('enables canonical results when canonizeResults is "true"', async () => { + interface Result { + __typename: string; + value: number; + } + + interface QueryData { + results: Result[]; + } + + const cache = new InMemoryCache({ + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); + + const query: TypedDocumentNode = gql` + query { + results { + value + } + } + `; + + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; + + cache.writeQuery({ + query, + data: { results }, + }); + + const client = new ApolloClient({ cache, link: new MockLink([]) }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { canonizeResults: true }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + const { snapshot } = await Profiler.takeRender(); + const resultSet = new Set(snapshot.result?.data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(snapshot.result).toEqual({ + data: { results }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + + expect(resultSet.size).toBe(5); + expect(values).toEqual([0, 1, 2, 3, 5]); +}); + +test("can disable canonical results when the cache's canonizeResults setting is true", async () => { + interface Result { + __typename: string; + value: number; + } + + const cache = new InMemoryCache({ + canonizeResults: true, + typePolicies: { + Result: { + keyFields: false, + }, + }, + }); + + const query: TypedDocumentNode<{ results: Result[] }, never> = gql` + query { + results { + value + } + } + `; + + const results: Result[] = [ + { __typename: "Result", value: 0 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 1 }, + { __typename: "Result", value: 2 }, + { __typename: "Result", value: 3 }, + { __typename: "Result", value: 5 }, + ]; + + cache.writeQuery({ + query, + data: { results }, + }); + + const client = new ApolloClient({ cache, link: new MockLink([]) }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query, { canonizeResults: false }); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + const { snapshot } = await Profiler.takeRender(); + const resultSet = new Set(snapshot.result!.data.results); + const values = Array.from(resultSet).map((item) => item.value); + + expect(snapshot.result).toEqual({ + data: { results }, + networkStatus: NetworkStatus.ready, + error: undefined, + }); + expect(resultSet.size).toBe(6); + expect(values).toEqual([0, 1, 1, 2, 3, 5]); +}); + +test("suspends deferred queries until initial chunk loads then rerenders with deferred data", async () => { + const query = gql` + query { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ cache: new InMemoryCache(), link }); + + const preloadQuery = createQueryPreloader(client); + const queryRef = preloadQuery(query); + + const { Profiler } = renderDefaultTestApp({ client, queryRef }); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["App", "SuspenseFallback"]); + } + + link.simulateResult({ + result: { + data: { greeting: { message: "Hello world", __typename: "Greeting" } }, + hasNext: true, + }, + }); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { greeting: { message: "Hello world", __typename: "Greeting" } }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } + + link.simulateResult( + { + result: { + incremental: [ + { + data: { + recipient: { name: "Alice", __typename: "Person" }, + __typename: "Greeting", + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + + { + const { snapshot, renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual(["ReadQueryHook"]); + expect(snapshot.result).toEqual({ + data: { + greeting: { + __typename: "Greeting", + message: "Hello world", + recipient: { __typename: "Person", name: "Alice" }, + }, + }, + error: undefined, + networkStatus: NetworkStatus.ready, + }); + } +}); + +describe.skip("type tests", () => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: new MockLink([]), + }); + const preloadQuery = createQueryPreloader(client); + + test("variables are optional and can be anything with untyped DocumentNode", () => { + const query = gql``; + + preloadQuery(query); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { variables: { foo: "bar" } }); + preloadQuery(query, { variables: { foo: "bar", bar: 2 } }); + }); + + test("variables are optional and can be anything with unspecified TVariables", () => { + type Data = { greeting: string }; + const query: TypedDocumentNode = gql``; + + preloadQuery(query); + preloadQuery(query); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { variables: { foo: "bar" } }); + preloadQuery(query, { variables: { foo: "bar" } }); + preloadQuery(query, { variables: { foo: "bar", bar: 2 } }); + preloadQuery(query, { variables: { foo: "bar", bar: 2 } }); + }); + + test("variables are optional when TVariables are empty", () => { + type Data = { greeting: string }; + type Variables = Record; + const query: TypedDocumentNode = gql``; + + preloadQuery(query); + preloadQuery(query); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { + returnPartialData: true, + variables: {}, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variables + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variables + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variables + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variables + foo: "bar", + }, + }); + }); + + test("does not allow variables when TVariables is `never`", () => { + type Data = { greeting: string }; + const query: TypedDocumentNode = gql``; + + preloadQuery(query); + preloadQuery(query); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { + returnPartialData: true, + variables: {}, + }); + // @ts-expect-error no variables allowed + preloadQuery(query, { variables: { foo: "bar" } }); + // @ts-expect-error no variables allowed + preloadQuery(query, { variables: { foo: "bar" } }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error no variables allowed + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error no variables allowed + foo: "bar", + }, + }); + }); + + test("optional variables are optional", () => { + type Data = { posts: string[] }; + type Variables = { limit?: number }; + const query: TypedDocumentNode = gql``; + + preloadQuery(query); + preloadQuery(query); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { variables: {} }); + preloadQuery(query, { returnPartialData: true, variables: {} }); + preloadQuery(query, { + returnPartialData: true, + variables: {}, + }); + preloadQuery(query, { variables: { limit: 10 } }); + preloadQuery(query, { variables: { limit: 10 } }); + preloadQuery(query, { returnPartialData: true, variables: { limit: 10 } }); + preloadQuery(query, { + returnPartialData: true, + variables: { limit: 10 }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + limit: 10, + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + limit: 10, + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + limit: 10, + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + limit: 10, + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + }); + + test("enforces required variables", () => { + type Data = { character: string }; + type Variables = { id: string }; + const query: TypedDocumentNode = gql``; + + // @ts-expect-error missing variables option + preloadQuery(query); + // @ts-expect-error missing variables option + preloadQuery(query); + // @ts-expect-error missing variables option + preloadQuery(query, { returnPartialData: true }); + // @ts-expect-error missing variables option + preloadQuery(query, { returnPartialData: true }); + preloadQuery(query, { + // @ts-expect-error empty variables + variables: {}, + }); + preloadQuery(query, { + // @ts-expect-error empty variables + variables: {}, + }); + preloadQuery(query, { + returnPartialData: true, + // @ts-expect-error empty variables + variables: {}, + }); + preloadQuery(query, { + returnPartialData: true, + // @ts-expect-error empty variables + variables: {}, + }); + preloadQuery(query, { variables: { id: "1" } }); + preloadQuery(query, { variables: { id: "1" } }); + preloadQuery(query, { returnPartialData: true, variables: { id: "1" } }); + preloadQuery(query, { + returnPartialData: true, + variables: { id: "1" }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + }); + + test("requires variables with mixed TVariables", () => { + type Data = { character: string }; + type Variables = { id: string; language?: string }; + const query: TypedDocumentNode = gql``; + + // @ts-expect-error missing variables argument + preloadQuery(query); + // @ts-expect-error missing variables argument + preloadQuery(query); + // @ts-expect-error missing variables argument + preloadQuery(query, {}); + // @ts-expect-error missing variables argument + preloadQuery(query, {}); + // @ts-expect-error missing variables option + preloadQuery(query, { returnPartialData: true }); + // @ts-expect-error missing variables option + preloadQuery(query, { returnPartialData: true }); + preloadQuery(query, { + // @ts-expect-error missing required variables + variables: {}, + }); + preloadQuery(query, { + // @ts-expect-error missing required variables + variables: {}, + }); + preloadQuery(query, { + returnPartialData: true, + // @ts-expect-error missing required variables + variables: {}, + }); + preloadQuery(query, { + returnPartialData: true, + // @ts-expect-error missing required variables + variables: {}, + }); + preloadQuery(query, { variables: { id: "1" } }); + preloadQuery(query, { variables: { id: "1" } }); + preloadQuery(query, { + // @ts-expect-error missing required variable + variables: { language: "en" }, + }); + preloadQuery(query, { + // @ts-expect-error missing required variable + variables: { language: "en" }, + }); + preloadQuery(query, { variables: { id: "1", language: "en" } }); + preloadQuery(query, { + variables: { id: "1", language: "en" }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + returnPartialData: true, + variables: { + id: "1", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + language: "en", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + preloadQuery(query, { + variables: { + id: "1", + language: "en", + // @ts-expect-error unknown variable + foo: "bar", + }, + }); + }); + + test("returns QueryReference when TData cannot be inferred", () => { + const query = gql``; + + const queryRef = preloadQuery(query); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + }); + + test("returns QueryReference in default case", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("returns QueryReference with errorPolicy: 'ignore'", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { errorPolicy: "ignore" }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + errorPolicy: "ignore", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("returns QueryReference with errorPolicy: 'all'", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { errorPolicy: "all" }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + errorPolicy: "all", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("returns QueryReference with errorPolicy: 'none'", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { errorPolicy: "none" }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + errorPolicy: "none", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("returns QueryReference> with returnPartialData: true", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { returnPartialData: true }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, { [key: string]: any }> + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: true, + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, OperationVariables> + >(); + } + }); + + test("returns QueryReference> with returnPartialData: false", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { returnPartialData: false }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: false, + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("returns QueryReference when passing an option unrelated to TData", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { canonizeResults: true }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + canonizeResults: true, + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef + >(); + } + }); + + test("handles combinations of options", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: true, + errorPolicy: "ignore", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef< + DeepPartial | undefined, + { [key: string]: any } + > + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: true, + errorPolicy: "ignore", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef< + DeepPartial | undefined, + OperationVariables + > + >(); + } + + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: true, + errorPolicy: "none", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, { [key: string]: any }> + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + returnPartialData: true, + errorPolicy: "none", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, OperationVariables> + >(); + } + }); + + test("returns correct TData type when combined with options unrelated to TData", () => { + { + const query: TypedDocumentNode = gql``; + const queryRef = preloadQuery(query, { + canonizeResults: true, + returnPartialData: true, + errorPolicy: "none", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, { [key: string]: any }> + >(); + } + + { + const query = gql``; + const queryRef = preloadQuery(query, { + canonizeResults: true, + returnPartialData: true, + errorPolicy: "none", + }); + + expectTypeOf(queryRef).toEqualTypeOf< + PreloadedQueryRef, OperationVariables> + >(); + } + }); +}); diff --git a/src/react/query-preloader/createQueryPreloader.ts b/src/react/query-preloader/createQueryPreloader.ts new file mode 100644 index 00000000000..226723dab9a --- /dev/null +++ b/src/react/query-preloader/createQueryPreloader.ts @@ -0,0 +1,192 @@ +import type { + ApolloClient, + DefaultContext, + DocumentNode, + ErrorPolicy, + OperationVariables, + RefetchWritePolicy, + TypedDocumentNode, + WatchQueryFetchPolicy, + WatchQueryOptions, +} from "../../core/index.js"; +import type { + DeepPartial, + OnlyRequiredProperties, +} from "../../utilities/index.js"; +import { InternalQueryReference, wrapQueryRef } from "../internal/index.js"; +import type { PreloadedQueryRef } from "../internal/index.js"; +import type { NoInfer } from "../index.js"; + +type VariablesOption = + [TVariables] extends [never] ? + { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ + variables?: Record; + } + : {} extends OnlyRequiredProperties ? + { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ + variables?: TVariables; + } + : { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ + variables: TVariables; + }; + +export type PreloadQueryFetchPolicy = Extract< + WatchQueryFetchPolicy, + "cache-first" | "network-only" | "no-cache" | "cache-and-network" +>; + +export type PreloadQueryOptions< + TVariables extends OperationVariables = OperationVariables, +> = { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ + canonizeResults?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ + context?: DefaultContext; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#errorPolicy:member} */ + errorPolicy?: ErrorPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#fetchPolicy:member} */ + fetchPolicy?: PreloadQueryFetchPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#returnPartialData:member} */ + returnPartialData?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#refetchWritePolicy:member} */ + refetchWritePolicy?: RefetchWritePolicy; +} & VariablesOption; + +type PreloadQueryOptionsArg< + TVariables extends OperationVariables, + TOptions = unknown, +> = [TVariables] extends [never] ? + [options?: PreloadQueryOptions & TOptions] +: {} extends OnlyRequiredProperties ? + [ + options?: PreloadQueryOptions> & + Omit, + ] +: [ + options: PreloadQueryOptions> & + Omit, + ]; + +/** + * A function that will begin loading a query when called. It's result can be + * read by `useReadQuery` which will suspend until the query is loaded. + * This is useful when you want to start loading a query as early as possible + * outside of a React component. + * + * @example + * ```js + * const preloadQuery = createQueryPreloader(client); + * const queryRef = preloadQuery(query, { variables, ...otherOptions }); + * + * function App() { + * return ( + * Loading}> + * + * + * ); + * } + * + * function MyQuery() { + * const { data } = useReadQuery(queryRef); + * + * // do something with `data` + * } + * ``` + */ +export interface PreloadQueryFunction { + /** {@inheritDoc @apollo/client!PreloadQueryFunction:interface} */ + < + TData, + TVariables extends OperationVariables, + TOptions extends Omit, + >( + query: DocumentNode | TypedDocumentNode, + ...[options]: PreloadQueryOptionsArg, TOptions> + ): PreloadedQueryRef< + TOptions["errorPolicy"] extends "ignore" | "all" ? + TOptions["returnPartialData"] extends true ? + DeepPartial | undefined + : TData | undefined + : TOptions["returnPartialData"] extends true ? DeepPartial + : TData, + TVariables + >; + + /** {@inheritDoc @apollo/client!PreloadQueryFunction:interface} */ + ( + query: DocumentNode | TypedDocumentNode, + options: PreloadQueryOptions> & { + returnPartialData: true; + errorPolicy: "ignore" | "all"; + } + ): PreloadedQueryRef | undefined, TVariables>; + + /** {@inheritDoc @apollo/client!PreloadQueryFunction:interface} */ + ( + query: DocumentNode | TypedDocumentNode, + options: PreloadQueryOptions> & { + errorPolicy: "ignore" | "all"; + } + ): PreloadedQueryRef; + + /** {@inheritDoc @apollo/client!PreloadQueryFunction:interface} */ + ( + query: DocumentNode | TypedDocumentNode, + options: PreloadQueryOptions> & { + returnPartialData: true; + } + ): PreloadedQueryRef, TVariables>; + + /** {@inheritDoc @apollo/client!PreloadQueryFunction:interface} */ + ( + query: DocumentNode | TypedDocumentNode, + ...[options]: PreloadQueryOptionsArg> + ): PreloadedQueryRef; +} + +/** + * A higher order function that returns a `preloadQuery` function which + * can be used to begin loading a query with the given `client`. This is useful + * when you want to start loading a query as early as possible outside of a + * React component. + * + * > Refer to the [Suspense - Initiating queries outside React](https://www.apollographql.com/docs/react/data/suspense#initiating-queries-outside-react) section for a more in-depth overview. + * + * @param client - The `ApolloClient` instance that will be used to load queries + * from the returned `preloadQuery` function. + * @returns The `preloadQuery` function. + * + * @example + * ```js + * const preloadQuery = createQueryPreloader(client); + * ``` + * @since 3.9.0 + */ +export function createQueryPreloader( + client: ApolloClient +): PreloadQueryFunction { + return function preloadQuery< + TData = unknown, + TVariables extends OperationVariables = OperationVariables, + >( + query: DocumentNode | TypedDocumentNode, + options: PreloadQueryOptions> & + VariablesOption = Object.create(null) + ): PreloadedQueryRef { + const queryRef = new InternalQueryReference( + client.watchQuery({ + ...options, + query, + } as WatchQueryOptions), + { + autoDisposeTimeoutMs: + client.defaultOptions.react?.suspense?.autoDisposeTimeoutMs, + } + ); + + return wrapQueryRef(queryRef) as PreloadedQueryRef; + }; +} diff --git a/src/react/ssr/RenderPromises.ts b/src/react/ssr/RenderPromises.ts index d72574bd6c1..44ca99519b7 100644 --- a/src/react/ssr/RenderPromises.ts +++ b/src/react/ssr/RenderPromises.ts @@ -1,7 +1,9 @@ -import type { DocumentNode } from "graphql"; +import type * as ReactTypes from "react"; import type { ObservableQuery, OperationVariables } from "../../core/index.js"; import type { QueryDataOptions } from "../types/types.js"; +import { Trie } from "@wry/trie"; +import { canonicalStringify } from "../../cache/index.js"; // TODO: A vestigial interface from when hooks were implemented with utility // classes, which should be deleted in the future. @@ -15,11 +17,13 @@ type QueryInfo = { observable: ObservableQuery | null; }; -function makeDefaultQueryInfo(): QueryInfo { - return { +function makeQueryInfoTrie() { + // these Tries are very short-lived, so we don't need to worry about making it + // "weak" - it's easier to test and debug as a strong Trie. + return new Trie(false, () => ({ seen: false, observable: null, - }; + })); } export class RenderPromises { @@ -30,13 +34,13 @@ export class RenderPromises { // objects. These QueryInfo objects are intended to survive through the whole // getMarkupFromTree process, whereas specific Query instances do not survive // beyond a single call to renderToStaticMarkup. - private queryInfoTrie = new Map>(); + private queryInfoTrie = makeQueryInfoTrie(); private stopped = false; public stop() { if (!this.stopped) { this.queryPromises.clear(); - this.queryInfoTrie.clear(); + this.queryInfoTrie = makeQueryInfoTrie(); this.stopped = true; } } @@ -58,8 +62,8 @@ export class RenderPromises { public addQueryPromise( queryInstance: QueryData, - finish?: () => React.ReactNode - ): React.ReactNode { + finish?: () => ReactTypes.ReactNode + ): ReactTypes.ReactNode { if (!this.stopped) { const info = this.lookupQueryInfo(queryInstance.getOptions()); if (!info.seen) { @@ -132,13 +136,9 @@ export class RenderPromises { private lookupQueryInfo( props: QueryDataOptions ): QueryInfo { - const { queryInfoTrie } = this; - const { query, variables } = props; - const varMap = queryInfoTrie.get(query) || new Map(); - if (!queryInfoTrie.has(query)) queryInfoTrie.set(query, varMap); - const variablesString = JSON.stringify(variables); - const info = varMap.get(variablesString) || makeDefaultQueryInfo(); - if (!varMap.has(variablesString)) varMap.set(variablesString, info); - return info; + return this.queryInfoTrie.lookup( + props.query, + canonicalStringify(props.variables) + ); } } diff --git a/src/react/ssr/__tests__/useQuery.test.tsx b/src/react/ssr/__tests__/useQuery.test.tsx index 3810f568575..02c711b9eba 100644 --- a/src/react/ssr/__tests__/useQuery.test.tsx +++ b/src/react/ssr/__tests__/useQuery.test.tsx @@ -2,12 +2,17 @@ import React from "react"; import { DocumentNode } from "graphql"; import gql from "graphql-tag"; -import { MockedProvider, mockSingleLink } from "../../../testing"; +import { + MockedProvider, + MockedResponse, + mockSingleLink, +} from "../../../testing"; import { ApolloClient } from "../../../core"; import { InMemoryCache } from "../../../cache"; -import { ApolloProvider } from "../../context"; +import { ApolloProvider, getApolloContext } from "../../context"; import { useApolloClient, useQuery } from "../../hooks"; import { renderToStringWithData } from ".."; +import type { Trie } from "@wry/trie"; describe("useQuery Hook SSR", () => { const CAR_QUERY: DocumentNode = gql` @@ -333,4 +338,50 @@ describe("useQuery Hook SSR", () => { expect(cache.extract()).toMatchSnapshot(); }); }); + + it("should deduplicate `variables` with identical content, but different order", async () => { + const mocks: MockedResponse[] = [ + { + request: { + query: CAR_QUERY, + variables: { foo: "a", bar: 1 }, + }, + result: { data: CAR_RESULT_DATA }, + maxUsageCount: 1, + }, + ]; + + let trie: Trie | undefined; + const Component = ({ + variables, + }: { + variables: { foo: string; bar: number }; + }) => { + const { loading, data } = useQuery(CAR_QUERY, { variables, ssr: true }); + trie ||= + React.useContext(getApolloContext()).renderPromises!["queryInfoTrie"]; + if (!loading) { + expect(data).toEqual(CAR_RESULT_DATA); + const { make, model, vin } = data.cars[0]; + return ( +
+ {make}, {model}, {vin} +
+ ); + } + return null; + }; + + await renderToStringWithData( + + <> + + + + + ); + expect( + Array.from(trie!["getChildTrie"](CAR_QUERY)["strong"].keys()) + ).toEqual(['{"bar":1,"foo":"a"}']); + }); }); diff --git a/src/react/ssr/getDataFromTree.ts b/src/react/ssr/getDataFromTree.ts index 49d706934c6..4dde58ec62a 100644 --- a/src/react/ssr/getDataFromTree.ts +++ b/src/react/ssr/getDataFromTree.ts @@ -1,10 +1,11 @@ -import * as React from "react"; +import * as React from "rehackt"; +import type * as ReactTypes from "react"; import { getApolloContext } from "../context/index.js"; import { RenderPromises } from "./RenderPromises.js"; import { renderToStaticMarkup } from "react-dom/server"; export function getDataFromTree( - tree: React.ReactNode, + tree: ReactTypes.ReactNode, context: { [key: string]: any } = {} ) { return getMarkupFromTree({ @@ -17,10 +18,10 @@ export function getDataFromTree( } export type GetMarkupFromTreeOptions = { - tree: React.ReactNode; + tree: ReactTypes.ReactNode; context?: { [key: string]: any }; renderFunction?: ( - tree: React.ReactElement + tree: ReactTypes.ReactElement ) => string | PromiseLike; }; @@ -51,8 +52,8 @@ export function getMarkupFromTree({ resolve(renderFunction(element)); }) .then((html) => { - return renderPromises.hasPromises() - ? renderPromises.consumeAndAwaitPromises().then(process) + return renderPromises.hasPromises() ? + renderPromises.consumeAndAwaitPromises().then(process) : html; }) .finally(() => { diff --git a/src/react/ssr/renderToStringWithData.ts b/src/react/ssr/renderToStringWithData.ts index 0e3944344a2..f6bcb345849 100644 --- a/src/react/ssr/renderToStringWithData.ts +++ b/src/react/ssr/renderToStringWithData.ts @@ -1,9 +1,9 @@ -import type { ReactElement } from "react"; +import type * as ReactTypes from "react"; import { getMarkupFromTree } from "./getDataFromTree.js"; import { renderToString } from "react-dom/server"; export function renderToStringWithData( - component: ReactElement + component: ReactTypes.ReactElement ): Promise { return getMarkupFromTree({ tree: component, diff --git a/src/react/types/types.documentation.ts b/src/react/types/types.documentation.ts new file mode 100644 index 00000000000..186d651dfd8 --- /dev/null +++ b/src/react/types/types.documentation.ts @@ -0,0 +1,598 @@ +export interface QueryOptionsDocumentation { + /** + * A GraphQL query string parsed into an AST with the gql template literal. + * + * @docGroup 1. Operation options + */ + query: unknown; + + /** + * An object containing all of the GraphQL variables your query requires to execute. + * + * Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value. + * + * @docGroup 1. Operation options + */ + variables: unknown; + + /** + * Specifies how the query handles a response that returns both GraphQL errors and partial results. + * + * For details, see [GraphQL error policies](https://www.apollographql.com/docs/react/data/error-handling/#graphql-error-policies). + * + * The default value is `none`, meaning that the query result includes error details but not partial results. + * + * @docGroup 1. Operation options + */ + errorPolicy: unknown; + + /** + * If you're using [Apollo Link](https://www.apollographql.com/docs/react/api/link/introduction/), this object is the initial value of the `context` object that's passed along your link chain. + * + * @docGroup 2. Networking options + */ + context: unknown; + + /** + * Specifies how the query interacts with the Apollo Client cache during execution (for example, whether it checks the cache for results before sending a request to the server). + * + * For details, see [Setting a fetch policy](https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy). + * + * The default value is `cache-first`. + * + * @docGroup 3. Caching options + */ + fetchPolicy: unknown; + + /** + * Specifies the `FetchPolicy` to be used after this query has completed. + * + * @docGroup 3. Caching options + */ + nextFetchPolicy: unknown; + + /** + * Defaults to the initial value of options.fetchPolicy, but can be explicitly + * configured to specify the WatchQueryFetchPolicy to revert back to whenever + * variables change (unless nextFetchPolicy intervenes). + * + * @docGroup 3. Caching options + */ + initialFetchPolicy: unknown; + + /** + * Specifies the interval (in milliseconds) at which the query polls for updated results. + * + * The default value is `0` (no polling). + * + * @docGroup 2. Networking options + */ + pollInterval: unknown; + + /** + * If `true`, the in-progress query's associated component re-renders whenever the network status changes or a network error occurs. + * + * The default value is `false`. + * + * @docGroup 2. Networking options + */ + notifyOnNetworkStatusChange: unknown; + + /** + * If `true`, the query can return partial results from the cache if the cache doesn't contain results for all queried fields. + * + * The default value is `false`. + * + * @docGroup 3. Caching options + */ + returnPartialData: unknown; + + /** + * Specifies whether a `NetworkStatus.refetch` operation should merge + * incoming field data with existing data, or overwrite the existing data. + * Overwriting is probably preferable, but merging is currently the default + * behavior, for backwards compatibility with Apollo Client 3.x. + * + * @docGroup 3. Caching options + */ + refetchWritePolicy: unknown; + + /** + * Watched queries must opt into overwriting existing data on refetch, by passing refetchWritePolicy: "overwrite" in their WatchQueryOptions. + * + * The default value is "overwrite". + * + * @docGroup 3. Caching options + */ + refetchWritePolicy_suspense: unknown; + + /** + * If `true`, causes a query refetch if the query result is detected as partial. + * + * The default value is `false`. + * + * @deprecated + * Setting this option is unnecessary in Apollo Client 3, thanks to a more consistent application of fetch policies. It might be removed in a future release. + */ + partialRefetch: unknown; + + /** + * Whether to canonize cache results before returning them. Canonization + * takes some extra time, but it speeds up future deep equality comparisons. + * Defaults to false. + * + * @deprecated + * Using `canonizeResults` can result in memory leaks so we generally do not + * recommend using this option anymore. + * A future version of Apollo Client will contain a similar feature without + * the risk of memory leaks. + */ + canonizeResults: unknown; + + /** + * If true, the query is not executed. + * + * The default value is `false`. + * + * @docGroup 1. Operation options + */ + skip: unknown; + + /** + * If `true`, the query is not executed. The default value is `false`. + * + * @deprecated We recommend using `skipToken` in place of the `skip` option as + * it is more type-safe. + * + * This option is deprecated and only supported to ease the migration from useQuery. It will be removed in a future release. + * + * @docGroup 1. Operation options + */ + skip_deprecated: unknown; + + /** + * A callback function that's called when your query successfully completes with zero errors (or if `errorPolicy` is `ignore` and partial data is returned). + * + * This function is passed the query's result `data`. + * + * @docGroup 1. Operation options + */ + onCompleted: unknown; + /** + * A callback function that's called when the query encounters one or more errors (unless `errorPolicy` is `ignore`). + * + * This function is passed an `ApolloError` object that contains either a `networkError` object or a `graphQLErrors` array, depending on the error(s) that occurred. + * + * @docGroup 1. Operation options + */ + onError: unknown; + + /** + * The instance of `ApolloClient` to use to execute the query. + * + * By default, the instance that's passed down via context is used, but you + * can provide a different instance here. + * + * @docGroup 1. Operation options + */ + client: unknown; + + /** + * A unique identifier for the query. Each item in the array must be a stable + * identifier to prevent infinite fetches. + * + * This is useful when using the same query and variables combination in more + * than one component, otherwise the components may clobber each other. This + * can also be used to force the query to re-evaluate fresh. + * + * @docGroup 1. Operation options + */ + queryKey: unknown; + + /** + * Pass `false` to skip executing the query during [server-side rendering](https://www.apollographql.com/docs/react/performance/server-side-rendering/). + * + * @docGroup 2. Networking options + */ + ssr: unknown; + + /** + * A callback function that's called whenever a refetch attempt occurs + * while polling. If the function returns `true`, the refetch is + * skipped and not reattempted until the next poll interval. + * + * @docGroup 2. Networking options + */ + skipPollAttempt: unknown; +} + +export interface QueryResultDocumentation { + /** + * The instance of Apollo Client that executed the query. + * Can be useful for manually executing followup queries or writing data to the cache. + * + * @docGroup 2. Network info + */ + client: unknown; + /** + * A reference to the internal `ObservableQuery` used by the hook. + */ + observable: unknown; + /** + * An object containing the result of your GraphQL query after it completes. + * + * This value might be `undefined` if a query results in one or more errors (depending on the query's `errorPolicy`). + * + * @docGroup 1. Operation data + */ + data: unknown; + /** + * An object containing the result from the most recent _previous_ execution of this query. + * + * This value is `undefined` if this is the query's first execution. + * + * @docGroup 1. Operation data + */ + previousData: unknown; + /** + * If the query produces one or more errors, this object contains either an array of `graphQLErrors` or a single `networkError`. Otherwise, this value is `undefined`. + * + * For more information, see [Handling operation errors](https://www.apollographql.com/docs/react/data/error-handling/). + * + * @docGroup 1. Operation data + */ + error: unknown; + /** + * If `true`, the query is still in flight and results have not yet been returned. + * + * @docGroup 2. Network info + */ + loading: unknown; + /** + * A number indicating the current network state of the query's associated request. [See possible values.](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/core/networkStatus.ts#L4) + * + * Used in conjunction with the [`notifyOnNetworkStatusChange`](#notifyonnetworkstatuschange) option. + * + * @docGroup 2. Network info + */ + networkStatus: unknown; + /** + * If `true`, the associated lazy query has been executed. + * + * This field is only present on the result object returned by [`useLazyQuery`](/react/data/queries/#executing-queries-manually). + * + * @docGroup 2. Network info + */ + called: unknown; + /** + * An object containing the variables that were provided for the query. + * + * @docGroup 1. Operation data + */ + variables: unknown; + + /** + * A function that enables you to re-execute the query, optionally passing in new `variables`. + * + * To guarantee that the refetch performs a network request, its `fetchPolicy` is set to `network-only` (unless the original query's `fetchPolicy` is `no-cache` or `cache-and-network`, which also guarantee a network request). + * + * See also [Refetching](https://www.apollographql.com/docs/react/data/queries/#refetching). + * + * @docGroup 3. Helper functions + */ + refetch: unknown; + /** + * {@inheritDoc @apollo/client!ObservableQuery#fetchMore:member(1)} + * + * @docGroup 3. Helper functions + */ + fetchMore: unknown; + /** + * {@inheritDoc @apollo/client!ObservableQuery#startPolling:member(1)} + * + * @docGroup 3. Helper functions + */ + startPolling: unknown; + /** + * {@inheritDoc @apollo/client!ObservableQuery#stopPolling:member(1)} + * + * @docGroup 3. Helper functions + */ + stopPolling: unknown; + /** + * {@inheritDoc @apollo/client!ObservableQuery#subscribeToMore:member(1)} + * + * @docGroup 3. Helper functions + */ + subscribeToMore: unknown; + /** + * {@inheritDoc @apollo/client!ObservableQuery#updateQuery:member(1)} + * + * @docGroup 3. Helper functions + */ + updateQuery: unknown; +} + +export interface MutationOptionsDocumentation { + /** + * A GraphQL document, often created with `gql` from the `graphql-tag` + * package, that contains a single mutation inside of it. + * + * @docGroup 1. Operation options + */ + mutation: unknown; + + /** + * Provide `no-cache` if the mutation's result should _not_ be written to the Apollo Client cache. + * + * The default value is `network-only` (which means the result _is_ written to the cache). + * + * Unlike queries, mutations _do not_ support [fetch policies](https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy) besides `network-only` and `no-cache`. + * + * @docGroup 3. Caching options + */ + fetchPolicy: unknown; + + /** + * To avoid retaining sensitive information from mutation root field + * arguments, Apollo Client v3.4+ automatically clears any `ROOT_MUTATION` + * fields from the cache after each mutation finishes. If you need this + * information to remain in the cache, you can prevent the removal by passing + * `keepRootFields: true` to the mutation. `ROOT_MUTATION` result data are + * also passed to the mutation `update` function, so we recommend obtaining + * the results that way, rather than using this option, if possible. + */ + keepRootFields: unknown; + + /** + * By providing either an object or a callback function that, when invoked after + * a mutation, allows you to return optimistic data and optionally skip updates + * via the `IGNORE` sentinel object, Apollo Client caches this temporary + * (and potentially incorrect) response until the mutation completes, enabling + * more responsive UI updates. + * + * For more information, see [Optimistic mutation results](https://www.apollographql.com/docs/react/performance/optimistic-ui/). + * + * @docGroup 3. Caching options + */ + optimisticResponse: unknown; + + /** + * A `MutationQueryReducersMap`, which is map from query names to + * mutation query reducers. Briefly, this map defines how to incorporate the + * results of the mutation into the results of queries that are currently + * being watched by your application. + */ + updateQueries: unknown; + + /** + * An array (or a function that _returns_ an array) that specifies which queries you want to refetch after the mutation occurs. + * + * Each array value can be either: + * + * - An object containing the `query` to execute, along with any `variables` + * + * - A string indicating the operation name of the query to refetch + * + * @docGroup 1. Operation options + */ + refetchQueries: unknown; + + /** + * If `true`, makes sure all queries included in `refetchQueries` are completed before the mutation is considered complete. + * + * The default value is `false` (queries are refetched asynchronously). + * + * @docGroup 1. Operation options + */ + awaitRefetchQueries: unknown; + + /** + * A function used to update the Apollo Client cache after the mutation completes. + * + * For more information, see [Updating the cache after a mutation](https://www.apollographql.com/docs/react/data/mutations#updating-the-cache-after-a-mutation). + * + * @docGroup 3. Caching options + */ + update: unknown; + + /** + * Optional callback for intercepting queries whose cache data has been updated by the mutation, as well as any queries specified in the `refetchQueries: [...]` list passed to `client.mutate`. + * + * Returning a `Promise` from `onQueryUpdated` will cause the final mutation `Promise` to await the returned `Promise`. Returning `false` causes the query to be ignored. + * + * @docGroup 1. Operation options + */ + onQueryUpdated: unknown; + + /** + * Specifies how the mutation handles a response that returns both GraphQL errors and partial results. + * + * For details, see [GraphQL error policies](https://www.apollographql.com/docs/react/data/error-handling/#graphql-error-policies). + * + * The default value is `none`, meaning that the mutation result includes error details but _not_ partial results. + * + * @docGroup 1. Operation options + */ + errorPolicy: unknown; + + /** + * An object containing all of the GraphQL variables your mutation requires to execute. + * + * Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value. + * + * @docGroup 1. Operation options + */ + variables: unknown; + + /** + * If you're using [Apollo Link](https://www.apollographql.com/docs/react/api/link/introduction/), this object is the initial value of the `context` object that's passed along your link chain. + * + * @docGroup 2. Networking options + */ + context: unknown; + + /** + * The instance of `ApolloClient` to use to execute the mutation. + * + * By default, the instance that's passed down via context is used, but you can provide a different instance here. + * + * @docGroup 2. Networking options + */ + client: unknown; + /** + * If `true`, the in-progress mutation's associated component re-renders whenever the network status changes or a network error occurs. + * + * The default value is `false`. + * + * @docGroup 2. Networking options + */ + notifyOnNetworkStatusChange: unknown; + /** + * A callback function that's called when your mutation successfully completes with zero errors (or if `errorPolicy` is `ignore` and partial data is returned). + * + * This function is passed the mutation's result `data` and any options passed to the mutation. + * + * @docGroup 1. Operation options + */ + onCompleted: unknown; + /** + * A callback function that's called when the mutation encounters one or more errors (unless `errorPolicy` is `ignore`). + * + * This function is passed an [`ApolloError`](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/errors/index.ts#L36-L39) object that contains either a `networkError` object or a `graphQLErrors` array, depending on the error(s) that occurred, as well as any options passed the mutation. + * + * @docGroup 1. Operation options + */ + onError: unknown; + /** + * If `true`, the mutation's `data` property is not updated with the mutation's result. + * + * The default value is `false`. + * + * @docGroup 1. Operation options + */ + ignoreResults: unknown; +} + +export interface MutationResultDocumentation { + /** + * The data returned from your mutation. Can be `undefined` if `ignoreResults` is `true`. + */ + data: unknown; + /** + * If the mutation produces one or more errors, this object contains either an array of `graphQLErrors` or a single `networkError`. Otherwise, this value is `undefined`. + * + * For more information, see [Handling operation errors](https://www.apollographql.com/docs/react/data/error-handling/). + */ + error: unknown; + /** + * If `true`, the mutation is currently in flight. + */ + loading: unknown; + /** + * If `true`, the mutation's mutate function has been called. + */ + called: unknown; + /** + * The instance of Apollo Client that executed the mutation. + * + * Can be useful for manually executing followup operations or writing data to the cache. + */ + client: unknown; + /** + * A function that you can call to reset the mutation's result to its initial, uncalled state. + */ + reset: unknown; +} + +export interface SubscriptionOptionsDocumentation { + /** + * A GraphQL document, often created with `gql` from the `graphql-tag` + * package, that contains a single subscription inside of it. + */ + query: unknown; + /** + * An object containing all of the variables your subscription needs to execute + */ + variables: unknown; + + /** + * Specifies the `ErrorPolicy` to be used for this operation + */ + errorPolicy: unknown; + + /** + * How you want your component to interact with the Apollo cache. For details, see [Setting a fetch policy](https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy). + */ + fetchPolicy: unknown; + + /** + * Determines if your subscription should be unsubscribed and subscribed again when an input to the hook (such as `subscription` or `variables`) changes. + */ + shouldResubscribe: unknown; + + /** + * An `ApolloClient` instance. By default `useSubscription` / `Subscription` uses the client passed down via context, but a different client can be passed in. + */ + client: unknown; + + /** + * Determines if the current subscription should be skipped. Useful if, for example, variables depend on previous queries and are not ready yet. + */ + skip: unknown; + + /** + * Shared context between your component and your network interface (Apollo Link). + */ + context: unknown; + + /** + * Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component completes the subscription. + * + * @since 3.7.0 + */ + onComplete: unknown; + + /** + * Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives data. The callback `options` object param consists of the current Apollo Client instance in `client`, and the received subscription data in `data`. + * + * @since 3.7.0 + */ + onData: unknown; + + /** + * Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives data. The callback `options` object param consists of the current Apollo Client instance in `client`, and the received subscription data in `subscriptionData`. + * + * @deprecated Use `onData` instead + */ + onSubscriptionData: unknown; + + /** + * Allows the registration of a callback function that will be triggered each time the `useSubscription` Hook / `Subscription` component receives an error. + * + * @since 3.7.0 + */ + onError: unknown; + + /** + * Allows the registration of a callback function that will be triggered when the `useSubscription` Hook / `Subscription` component completes the subscription. + * + * @deprecated Use `onComplete` instead + */ + onSubscriptionComplete: unknown; +} + +export interface SubscriptionResultDocumentation { + /** + * A boolean that indicates whether any initial data has been returned + */ + loading: unknown; + /** + * An object containing the result of your GraphQL subscription. Defaults to an empty object. + */ + data: unknown; + /** + * A runtime error with `graphQLErrors` and `networkError` properties + */ + error: unknown; +} diff --git a/src/react/types/types.ts b/src/react/types/types.ts index 5d057261dbc..41cff9e8835 100644 --- a/src/react/types/types.ts +++ b/src/react/types/types.ts @@ -1,4 +1,4 @@ -import type { ReactNode } from "react"; +import type * as ReactTypes from "react"; import type { DocumentNode } from "graphql"; import type { TypedDocumentNode } from "@graphql-typed-document-node/core"; @@ -13,18 +13,30 @@ import type { ApolloClient, DefaultContext, FetchPolicy, - MutationOptions, NetworkStatus, ObservableQuery, OperationVariables, InternalRefetchQueriesInclude, WatchQueryOptions, WatchQueryFetchPolicy, + SubscribeToMoreOptions, + ApolloQueryResult, + FetchMoreQueryOptions, + ErrorPolicy, + RefetchWritePolicy, } from "../../core/index.js"; +import type { + MutationSharedOptions, + SharedWatchQueryOptions, +} from "../../core/watchQueryOptions.js"; /* QueryReference type */ -export type { QueryReference } from "../cache/QueryReference.js"; +export type { + QueryReference, + QueryRef, + PreloadedQueryRef, +} from "../internal/index.js"; /* Common types */ @@ -38,18 +50,25 @@ export type CommonOptions = TOptions & { export interface BaseQueryOptions< TVariables extends OperationVariables = OperationVariables, -> extends Omit, "query"> { + TData = any, +> extends SharedWatchQueryOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#ssr:member} */ ssr?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#client:member} */ client?: ApolloClient; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ context?: DefaultContext; } export interface QueryFunctionOptions< TData = any, TVariables extends OperationVariables = OperationVariables, -> extends BaseQueryOptions { +> extends BaseQueryOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#skip:member} */ skip?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#onCompleted:member} */ onCompleted?: (data: TData) => void; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#onError:member} */ onError?: (error: ApolloError) => void; // Default WatchQueryOptions for this useQuery, providing initial values for @@ -57,35 +76,83 @@ export interface QueryFunctionOptions< // by option, not whole), but never overriding options previously passed to // useQuery (or options added/modified later by other means). // TODO What about about default values that are expensive to evaluate? + /** @internal */ defaultOptions?: Partial>; } -export type ObservableQueryFields< +export interface ObservableQueryFields< TData, TVariables extends OperationVariables, -> = Pick< - ObservableQuery, - | "startPolling" - | "stopPolling" - | "subscribeToMore" - | "updateQuery" - | "refetch" - | "reobserve" - | "variables" - | "fetchMore" ->; +> { + /** {@inheritDoc @apollo/client!QueryResultDocumentation#startPolling:member} */ + startPolling: (pollInterval: number) => void; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#stopPolling:member} */ + stopPolling: () => void; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#subscribeToMore:member} */ + subscribeToMore: < + TSubscriptionData = TData, + TSubscriptionVariables extends OperationVariables = TVariables, + >( + options: SubscribeToMoreOptions< + TData, + TSubscriptionVariables, + TSubscriptionData + > + ) => () => void; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#updateQuery:member} */ + updateQuery: ( + mapFn: ( + previousQueryResult: TData, + options: Pick, "variables"> + ) => TData + ) => void; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#refetch:member} */ + refetch: ( + variables?: Partial + ) => Promise>; + /** @internal */ + reobserve: ( + newOptions?: Partial>, + newNetworkStatus?: NetworkStatus + ) => Promise>; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#variables:member} */ + variables: TVariables | undefined; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#fetchMore:member} */ + fetchMore: < + TFetchData = TData, + TFetchVars extends OperationVariables = TVariables, + >( + fetchMoreOptions: FetchMoreQueryOptions & { + updateQuery?: ( + previousQueryResult: TData, + options: { + fetchMoreResult: TFetchData; + variables: TFetchVars; + } + ) => TData; + } + ) => Promise>; +} export interface QueryResult< TData = any, TVariables extends OperationVariables = OperationVariables, > extends ObservableQueryFields { + /** {@inheritDoc @apollo/client!QueryResultDocumentation#client:member} */ client: ApolloClient; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#observable:member} */ observable: ObservableQuery; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#data:member} */ data: TData | undefined; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#previousData:member} */ previousData?: TData; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#error:member} */ error?: ApolloError; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#loading:member} */ loading: boolean; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#networkStatus:member} */ networkStatus: NetworkStatus; + /** {@inheritDoc @apollo/client!QueryResultDocumentation#called:member} */ called: boolean; } @@ -93,7 +160,8 @@ export interface QueryDataOptions< TData = any, TVariables extends OperationVariables = OperationVariables, > extends QueryFunctionOptions { - children?: (result: QueryResult) => ReactNode; + children?: (result: QueryResult) => ReactTypes.ReactNode; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#query:member} */ query: DocumentNode | TypedDocumentNode; } @@ -105,8 +173,15 @@ export interface QueryHookOptions< export interface LazyQueryHookOptions< TData = any, TVariables extends OperationVariables = OperationVariables, -> extends Omit, "skip"> {} +> extends BaseQueryOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#onCompleted:member} */ + onCompleted?: (data: TData) => void; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#onError:member} */ + onError?: (error: ApolloError) => void; + /** @internal */ + defaultOptions?: Partial>; +} export interface LazyQueryHookExecOptions< TData = any, TVariables extends OperationVariables = OperationVariables, @@ -122,24 +197,28 @@ export type SuspenseQueryHookFetchPolicy = Extract< export interface SuspenseQueryHookOptions< TData = unknown, TVariables extends OperationVariables = OperationVariables, -> extends Pick< - QueryHookOptions, - | "client" - | "variables" - | "errorPolicy" - | "context" - | "canonizeResults" - | "returnPartialData" - | "refetchWritePolicy" - > { +> { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#client:member} */ + client?: ApolloClient; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ + context?: DefaultContext; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ + variables?: TVariables; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#errorPolicy:member} */ + errorPolicy?: ErrorPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ + canonizeResults?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#returnPartialData:member} */ + returnPartialData?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#refetchWritePolicy_suspense:member} */ + refetchWritePolicy?: RefetchWritePolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: SuspenseQueryHookFetchPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#queryKey:member} */ queryKey?: string | number | any[]; /** - * If `true`, the query is not executed. The default value is `false`. - * - * @deprecated We recommend using `skipToken` in place of the `skip` option as - * it is more type-safe. + * {@inheritDoc @apollo/client!QueryOptionsDocumentation#skip_deprecated:member} * * @example Recommended usage of `skipToken`: * ```ts @@ -173,10 +252,7 @@ export interface BackgroundQueryHookOptions< queryKey?: string | number | any[]; /** - * If `true`, the query is not executed. The default value is `false`. - * - * @deprecated We recommend using `skipToken` in place of the `skip` option as - * it is more type-safe. + * {@inheritDoc @apollo/client!QueryOptionsDocumentation#skip_deprecated:member} * * @example Recommended usage of `skipToken`: * ```ts @@ -188,16 +264,42 @@ export interface BackgroundQueryHookOptions< skip?: boolean; } +export type LoadableQueryHookFetchPolicy = Extract< + WatchQueryFetchPolicy, + "cache-first" | "network-only" | "no-cache" | "cache-and-network" +>; + +export interface LoadableQueryHookOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#canonizeResults:member} */ + canonizeResults?: boolean; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#client:member} */ + client?: ApolloClient; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ + context?: DefaultContext; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#errorPolicy:member} */ + errorPolicy?: ErrorPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#fetchPolicy:member} */ + fetchPolicy?: LoadableQueryHookFetchPolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#queryKey:member} */ + queryKey?: string | number | any[]; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#refetchWritePolicy:member} */ + refetchWritePolicy?: RefetchWritePolicy; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#returnPartialData:member} */ + returnPartialData?: boolean; +} + /** - * @deprecated TODO Delete this unused interface. + * @deprecated This type will be removed in the next major version of Apollo Client */ export interface QueryLazyOptions { + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#variables:member} */ variables?: TVariables; + /** {@inheritDoc @apollo/client!QueryOptionsDocumentation#context:member} */ context?: DefaultContext; } /** - * @deprecated TODO Delete this unused type alias. + * @deprecated This type will be removed in the next major version of Apollo Client */ export type LazyQueryResult< TData, @@ -205,7 +307,7 @@ export type LazyQueryResult< > = QueryResult; /** - * @deprecated TODO Delete this unused type alias. + * @deprecated This type will be removed in the next major version of Apollo Client */ export type QueryTuple< TData, @@ -222,7 +324,10 @@ export type LazyQueryExecFunction< export type LazyQueryResultTuple< TData, TVariables extends OperationVariables, -> = [LazyQueryExecFunction, QueryResult]; +> = [ + execute: LazyQueryExecFunction, + result: QueryResult, +]; /* Mutation types */ @@ -235,14 +340,16 @@ export interface BaseMutationOptions< TVariables = OperationVariables, TContext = DefaultContext, TCache extends ApolloCache = ApolloCache, -> extends Omit< - MutationOptions, - "mutation" - > { +> extends MutationSharedOptions { + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#client:member} */ client?: ApolloClient; + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#notifyOnNetworkStatusChange:member} */ notifyOnNetworkStatusChange?: boolean; + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#onCompleted:member} */ onCompleted?: (data: TData, clientOptions?: BaseMutationOptions) => void; + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#onError:member} */ onError?: (error: ApolloError, clientOptions?: BaseMutationOptions) => void; + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#ignoreResults:member} */ ignoreResults?: boolean; } @@ -252,15 +359,22 @@ export interface MutationFunctionOptions< TContext = DefaultContext, TCache extends ApolloCache = ApolloCache, > extends BaseMutationOptions { + /** {@inheritDoc @apollo/client!MutationOptionsDocumentation#mutation:member} */ mutation?: DocumentNode | TypedDocumentNode; } export interface MutationResult { + /** {@inheritDoc @apollo/client!MutationResultDocumentation#data:member} */ data?: TData | null; + /** {@inheritDoc @apollo/client!MutationResultDocumentation#error:member} */ error?: ApolloError; + /** {@inheritDoc @apollo/client!MutationResultDocumentation#loading:member} */ loading: boolean; + /** {@inheritDoc @apollo/client!MutationResultDocumentation#called:member} */ called: boolean; + /** {@inheritDoc @apollo/client!MutationResultDocumentation#client:member} */ client: ApolloClient; + /** {@inheritDoc @apollo/client!MutationResultDocumentation#reset:member} */ reset(): void; } @@ -295,12 +409,12 @@ export type MutationTuple< TContext = DefaultContext, TCache extends ApolloCache = ApolloCache, > = [ - ( + mutate: ( options?: MutationFunctionOptions // TODO This FetchResult seems strange here, as opposed to an // ApolloQueryResult ) => Promise>, - MutationResult, + result: MutationResult, ]; /* Subscription types */ @@ -319,33 +433,44 @@ export interface BaseSubscriptionOptions< TData = any, TVariables extends OperationVariables = OperationVariables, > { + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#variables:member} */ variables?: TVariables; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#fetchPolicy:member} */ fetchPolicy?: FetchPolicy; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#shouldResubscribe:member} */ shouldResubscribe?: | boolean | ((options: BaseSubscriptionOptions) => boolean); + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#client:member} */ client?: ApolloClient; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#skip:member} */ skip?: boolean; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#context:member} */ context?: DefaultContext; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onComplete:member} */ onComplete?: () => void; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onData:member} */ onData?: (options: OnDataOptions) => any; - /** - * @deprecated Use onData instead - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onSubscriptionData:member} */ onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onError:member} */ onError?: (error: ApolloError) => void; - /** - * @deprecated Use onComplete instead - */ + /** {@inheritDoc @apollo/client!SubscriptionOptionsDocumentation#onSubscriptionComplete:member} */ onSubscriptionComplete?: () => void; } export interface SubscriptionResult { + /** {@inheritDoc @apollo/client!SubscriptionResultDocumentation#loading:member} */ loading: boolean; + /** {@inheritDoc @apollo/client!SubscriptionResultDocumentation#data:member} */ data?: TData; + /** {@inheritDoc @apollo/client!SubscriptionResultDocumentation#error:member} */ error?: ApolloError; // This was added by the legacy useSubscription type, and is tested in unit // tests, but probably shouldn’t be added to the result. + /** + * @internal + */ variables?: TVariables; } @@ -359,7 +484,9 @@ export interface SubscriptionDataOptions< TVariables extends OperationVariables = OperationVariables, > extends BaseSubscriptionOptions { subscription: DocumentNode | TypedDocumentNode; - children?: null | ((result: SubscriptionResult) => JSX.Element | null); + children?: + | null + | ((result: SubscriptionResult) => ReactTypes.ReactNode); } export interface SubscriptionCurrentObservable { diff --git a/src/testing/core/itAsync.ts b/src/testing/core/itAsync.ts index 80fffe8f181..fc664cbac07 100644 --- a/src/testing/core/itAsync.ts +++ b/src/testing/core/itAsync.ts @@ -9,7 +9,7 @@ function wrap(key?: "only" | "skip" | "todo") { ) => (key ? it[key] : it)( message, - function () { + function (this: unknown) { return new Promise((resolve, reject) => callback.call(this, resolve, reject) ); @@ -21,7 +21,7 @@ function wrap(key?: "only" | "skip" | "todo") { const wrappedIt = wrap(); export const itAsync = Object.assign( - function (...args: Parameters) { + function (this: unknown, ...args: Parameters) { return wrappedIt.apply(this, args); }, { diff --git a/src/testing/core/mocking/__tests__/mockLink.ts b/src/testing/core/mocking/__tests__/mockLink.ts new file mode 100644 index 00000000000..4207a42503a --- /dev/null +++ b/src/testing/core/mocking/__tests__/mockLink.ts @@ -0,0 +1,314 @@ +import gql from "graphql-tag"; +import { MockLink, MockedResponse } from "../mockLink"; +import { execute } from "../../../../link/core/execute"; +import { ObservableStream } from "../../../internal"; + +describe("MockedResponse.newData", () => { + const setup = () => { + const weaklyTypedMockResponse: MockedResponse = { + request: { + query: gql` + query A { + a + } + `, + }, + }; + + const stronglyTypedMockResponse: MockedResponse< + { a: string }, + { input: string } + > = { + request: { + query: gql` + query A { + a + } + `, + }, + }; + + return { + weaklyTypedMockResponse, + stronglyTypedMockResponse, + }; + }; + + test("returned 'data' can be any object with untyped response", () => { + const { weaklyTypedMockResponse } = setup(); + + weaklyTypedMockResponse.newData = ({ fake: { faker } }) => ({ + data: { + pretend: faker, + }, + }); + }); + + test("can't return output that doesn't match TData", () => { + const { stronglyTypedMockResponse } = setup(); + + // @ts-expect-error return type does not match `TData` + stronglyTypedMockResponse.newData = () => ({ + data: { + a: 123, + }, + }); + }); + + test("can't use input variables that don't exist in TVariables", () => { + const { stronglyTypedMockResponse } = setup(); + + // @ts-expect-error unknown variables + stronglyTypedMockResponse.newData = ({ fake: { faker } }) => ({ + data: { + a: faker, + }, + }); + }); +}); + +/* +We've chosen this value as the MAXIMUM_DELAY since values that don't fit into a 32-bit signed int cause setTimeout to fire immediately +*/ +const MAXIMUM_DELAY = 0x7f_ff_ff_ff; + +describe("mockLink", () => { + beforeAll(() => jest.useFakeTimers()); + afterAll(() => jest.useRealTimers()); + + const query = gql` + query A { + a + } + `; + + it("should not require a result or error when delay equals Infinity", async () => { + const mockLink = new MockLink([ + { + request: { + query, + }, + delay: Infinity, + }, + ]); + + const observable = execute(mockLink, { query }); + + const subscription = observable.subscribe( + () => fail("onNext was called"), + () => fail("onError was called"), + () => fail("onComplete was called") + ); + jest.advanceTimersByTime(MAXIMUM_DELAY); + subscription.unsubscribe(); + }); + + it("should require result or error when delay is just large", (done) => { + const mockLink = new MockLink([ + { + request: { + query, + }, + delay: MAXIMUM_DELAY, + }, + ]); + + execute(mockLink, { query }).subscribe( + () => fail("onNext was called"), + (error) => { + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatch( + /^Mocked response should contain either `result`, `error` or a `delay` of `Infinity`: / + ); + done(); + } + ); + + jest.advanceTimersByTime(MAXIMUM_DELAY); + }); +}); + +test("removes @nonreactive directives from fields", async () => { + const serverQuery = gql` + query A { + a + b + c + } + `; + + const link = new MockLink([ + { + request: { + query: gql` + query A { + a + b + c @nonreactive + } + `, + }, + result: { data: { a: 1, b: 2, c: 3 } }, + }, + { + request: { + query: gql` + query A { + a + b + c + } + `, + }, + result: { data: { a: 4, b: 5, c: 6 } }, + }, + ]); + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ + data: { a: 1, b: 2, c: 3 }, + }); + } + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ + data: { a: 4, b: 5, c: 6 }, + }); + } +}); + +test("removes @connection directives", async () => { + const serverQuery = gql` + query A { + a + b + c + } + `; + + const link = new MockLink([ + { + request: { + query: gql` + query A { + a + b + c @connection(key: "test") + } + `, + }, + result: { data: { a: 1, b: 2, c: 3 } }, + }, + { + request: { + query: gql` + query A { + a + b + c + } + `, + }, + result: { data: { a: 4, b: 5, c: 6 } }, + }, + ]); + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ + data: { a: 1, b: 2, c: 3 }, + }); + } + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ + data: { a: 4, b: 5, c: 6 }, + }); + } +}); + +test("removes fields with @client directives", async () => { + const serverQuery = gql` + query A { + a + b + } + `; + + const link = new MockLink([ + { + request: { + query: gql` + query A { + a + b + c @client + } + `, + }, + result: { data: { a: 1, b: 2 } }, + }, + { + request: { + query: gql` + query A { + a + b + } + `, + }, + result: { data: { a: 3, b: 4 } }, + }, + ]); + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ data: { a: 1, b: 2 } }); + } + + { + const stream = new ObservableStream(execute(link, { query: serverQuery })); + + await expect(stream.takeNext()).resolves.toEqual({ data: { a: 3, b: 4 } }); + } +}); + +describe.skip("type tests", () => { + const ANY = {} as any; + test("covariant behaviour: `MockedResponses` should be assignable to `MockedResponse`", () => { + let unspecificArray: MockedResponse[] = []; + let specificArray: MockedResponse<{ foo: string }, { foo: string }>[] = []; + let unspecificResponse: MockedResponse = ANY; + let specificResponse: MockedResponse<{ foo: string }, { foo: string }> = + ANY; + + unspecificArray.push(specificResponse); + unspecificArray.push(unspecificResponse); + + specificArray.push(specificResponse); + // @ts-expect-error + specificArray.push(unspecificResponse); + + unspecificArray = [specificResponse]; + unspecificArray = [unspecificResponse]; + unspecificArray = [specificResponse, unspecificResponse]; + + specificArray = [specificResponse]; + // @ts-expect-error + specificArray = [unspecificResponse]; + // @ts-expect-error + specificArray = [specificResponse, unspecificResponse]; + + unspecificResponse = specificResponse; + // @ts-expect-error + specificResponse = unspecificResponse; + }); +}); diff --git a/src/testing/core/mocking/mockFetch.ts b/src/testing/core/mocking/mockFetch.ts index 1b9d3daef89..95a047c4c38 100644 --- a/src/testing/core/mocking/mockFetch.ts +++ b/src/testing/core/mocking/mockFetch.ts @@ -105,9 +105,12 @@ function sortByKey(obj: any): Object { Object.assign( { [key]: - Object.prototype.toString.call(obj[key]).slice(8, -1) === "Object" - ? sortByKey(obj[key]) - : obj[key], + ( + Object.prototype.toString.call(obj[key]).slice(8, -1) === + "Object" + ) ? + sortByKey(obj[key]) + : obj[key], }, ret ), diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts index e02d8aaf794..f38474e1c20 100644 --- a/src/testing/core/mocking/mockLink.ts +++ b/src/testing/core/mocking/mockLink.ts @@ -13,23 +13,37 @@ import { Observable, addTypenameToDocument, removeClientSetsFromDocument, - removeConnectionDirectiveFromDocument, cloneDeep, stringifyForDisplay, print, + removeDirectivesFromDocument, + checkDocument, } from "../../../utilities/index.js"; -export type ResultFunction = () => T; +/** @internal */ +type CovariantUnaryFunction = { fn(arg: Arg): Ret }["fn"]; + +export type ResultFunction> = CovariantUnaryFunction< + V, + T +>; + +export type VariableMatcher> = CovariantUnaryFunction< + V, + boolean +>; export interface MockedResponse< - TData = Record, - TVariables = Record, + out TData = Record, + out TVariables = Record, > { request: GraphQLRequest; - result?: FetchResult | ResultFunction>; + maxUsageCount?: number; + result?: FetchResult | ResultFunction, TVariables>; error?: Error; delay?: number; - newData?: ResultFunction; + variableMatcher?: VariableMatcher; + newData?: ResultFunction, TVariables>; } export interface MockLinkOptions { @@ -45,13 +59,13 @@ function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { } export class MockLink extends ApolloLink { - public operation: Operation; + public operation!: Operation; public addTypename: Boolean = true; public showWarnings: boolean = true; private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; constructor( - mockedResponses: ReadonlyArray, + mockedResponses: ReadonlyArray>, addTypename: Boolean = true, options: MockLinkOptions = Object.create(null) ) { @@ -87,12 +101,16 @@ export class MockLink extends ApolloLink { const unmatchedVars: Array> = []; const requestVariables = operation.variables || {}; const mockedResponses = this.mockedResponsesByKey[key]; - const responseIndex = mockedResponses - ? mockedResponses.findIndex((res, index) => { + const responseIndex = + mockedResponses ? + mockedResponses.findIndex((res, index) => { const mockedResponseVars = res.request.variables || {}; if (equal(requestVariables, mockedResponseVars)) { return true; } + if (res.variableMatcher && res.variableMatcher(operation.variables)) { + return true; + } unmatchedVars.push(mockedResponseVars); return false; }) @@ -101,6 +119,12 @@ export class MockLink extends ApolloLink { const response = responseIndex >= 0 ? mockedResponses[responseIndex] : void 0; + // There have been platform- and engine-dependent differences with + // setInterval(fn, Infinity), so we pass 0 instead (but detect + // Infinity where we call observer.error or observer.next to pend + // indefinitely in those cases.) + const delay = response?.delay === Infinity ? 0 : response?.delay ?? 0; + let configError: Error; if (!response) { @@ -108,14 +132,14 @@ export class MockLink extends ApolloLink { `No more mocked responses for the query: ${print(operation.query)} Expected variables: ${stringifyForDisplay(operation.variables)} ${ - unmatchedVars.length > 0 - ? ` + unmatchedVars.length > 0 ? + ` Failed to match ${unmatchedVars.length} mock${ - unmatchedVars.length === 1 ? "" : "s" - } for this query. The mocked response had the following variables: + unmatchedVars.length === 1 ? "" : "s" + } for this query. The mocked response had the following variables: ${unmatchedVars.map((d) => ` ${stringifyForDisplay(d)}`).join("\n")} ` - : "" + : "" }` ); @@ -127,54 +151,54 @@ ${unmatchedVars.map((d) => ` ${stringifyForDisplay(d)}`).join("\n")} ); } } else { - mockedResponses.splice(responseIndex, 1); - + if (response.maxUsageCount && response.maxUsageCount > 1) { + response.maxUsageCount--; + } else { + mockedResponses.splice(responseIndex, 1); + } const { newData } = response; if (newData) { - response.result = newData(); + response.result = newData(operation.variables); mockedResponses.push(response); } - if (!response.result && !response.error) { + if (!response.result && !response.error && response.delay !== Infinity) { configError = new Error( - `Mocked response should contain either result or error: ${key}` + `Mocked response should contain either \`result\`, \`error\` or a \`delay\` of \`Infinity\`: ${key}` ); } } return new Observable((observer) => { - const timer = setTimeout( - () => { - if (configError) { - try { - // The onError function can return false to indicate that - // configError need not be passed to observer.error. For - // example, the default implementation of onError calls - // observer.error(configError) and then returns false to - // prevent this extra (harmless) observer.error call. - if (this.onError(configError, observer) !== false) { - throw configError; - } - } catch (error) { - observer.error(error); + const timer = setTimeout(() => { + if (configError) { + try { + // The onError function can return false to indicate that + // configError need not be passed to observer.error. For + // example, the default implementation of onError calls + // observer.error(configError) and then returns false to + // prevent this extra (harmless) observer.error call. + if (this.onError(configError, observer) !== false) { + throw configError; } - } else if (response) { - if (response.error) { - observer.error(response.error); - } else { - if (response.result) { - observer.next( - typeof response.result === "function" - ? (response.result as ResultFunction)() - : response.result - ); - } - observer.complete(); + } catch (error) { + observer.error(error); + } + } else if (response && response.delay !== Infinity) { + if (response.error) { + observer.error(response.error); + } else { + if (response.result) { + observer.next( + typeof response.result === "function" ? + response.result(operation.variables) + : response.result + ); } + observer.complete(); } - }, - (response && response.delay) || 0 - ); + } + }, delay); return () => { clearTimeout(timer); @@ -186,17 +210,44 @@ ${unmatchedVars.map((d) => ` ${stringifyForDisplay(d)}`).join("\n")} mockedResponse: MockedResponse ): MockedResponse { const newMockedResponse = cloneDeep(mockedResponse); - const queryWithoutConnection = removeConnectionDirectiveFromDocument( - newMockedResponse.request.query + const queryWithoutClientOnlyDirectives = removeDirectivesFromDocument( + [{ name: "connection" }, { name: "nonreactive" }], + checkDocument(newMockedResponse.request.query) ); - invariant(queryWithoutConnection, "query is required"); - newMockedResponse.request.query = queryWithoutConnection!; + invariant(queryWithoutClientOnlyDirectives, "query is required"); + newMockedResponse.request.query = queryWithoutClientOnlyDirectives!; const query = removeClientSetsFromDocument(newMockedResponse.request.query); if (query) { newMockedResponse.request.query = query; } + + mockedResponse.maxUsageCount = mockedResponse.maxUsageCount ?? 1; + invariant( + mockedResponse.maxUsageCount > 0, + `Mock response maxUsageCount must be greater than 0, %s given`, + mockedResponse.maxUsageCount + ); + + this.normalizeVariableMatching(newMockedResponse); return newMockedResponse; } + + private normalizeVariableMatching(mockedResponse: MockedResponse) { + const variables = mockedResponse.request.variables; + if (mockedResponse.variableMatcher && variables) { + throw new Error( + "Mocked response should contain either variableMatcher or request.variables" + ); + } + + if (!mockedResponse.variableMatcher) { + mockedResponse.variableMatcher = (vars) => { + const requestVariables = vars || {}; + const mockedResponseVariables = variables || {}; + return equal(requestVariables, mockedResponseVariables); + }; + } + } } export interface MockApolloLink extends ApolloLink { diff --git a/src/testing/core/mocking/mockSubscriptionLink.ts b/src/testing/core/mocking/mockSubscriptionLink.ts index 93e48d5ddb4..b91c86e8b68 100644 --- a/src/testing/core/mocking/mockSubscriptionLink.ts +++ b/src/testing/core/mocking/mockSubscriptionLink.ts @@ -15,7 +15,7 @@ export interface MockedSubscriptionResult { export class MockSubscriptionLink extends ApolloLink { public unsubscribers: any[] = []; public setups: any[] = []; - public operation: Operation; + public operation?: Operation; private observers: any[] = []; diff --git a/src/testing/core/observableToPromise.ts b/src/testing/core/observableToPromise.ts index 4d8415d4838..428517e1aff 100644 --- a/src/testing/core/observableToPromise.ts +++ b/src/testing/core/observableToPromise.ts @@ -1,21 +1,26 @@ import type { ObservableQuery, ApolloQueryResult } from "../../core/index.js"; import type { ObservableSubscription } from "../../utilities/index.js"; -/** - * - * @param observable the observable query to subscribe to - * @param shouldResolve should we resolve after seeing all our callbacks [default: true] - * (use this if you are racing the promise against another) - * @param wait how long to wait after seeing desired callbacks before resolving - * [default: -1 => don't wait] - * @param errorCallbacks an expected set of errors - */ -export type Options = { - observable: ObservableQuery; +export interface Options { + /** + * The ObservableQuery to subscribe to. + */ + observable: ObservableQuery; + /** + * Should we resolve after seeing all our callbacks? [default: true] + * (use this if you are racing the promise against another) + */ shouldResolve?: boolean; + /** + * How long to wait after seeing desired callbacks before resolving? + * [default: -1 => don't wait] + */ wait?: number; + /** + * An expected set of errors. + */ errorCallbacks?: ((error: Error) => any)[]; -}; +} export type ResultCallback = (result: ApolloQueryResult) => any; diff --git a/src/testing/core/withConsoleSpy.ts b/src/testing/core/withConsoleSpy.ts index c5c425e6e33..01626d7f5f9 100644 --- a/src/testing/core/withConsoleSpy.ts +++ b/src/testing/core/withConsoleSpy.ts @@ -2,8 +2,7 @@ function wrapTestFunction( fn: (...args: any[]) => any, consoleMethodName: "log" | "warn" | "error" ) { - return function () { - const args = arguments; + return function (this: any, ...args: any[]) { const spy = jest.spyOn(console, consoleMethodName); spy.mockImplementation(() => {}); return new Promise((resolve) => { diff --git a/src/testing/experimental/__tests__/createTestSchema.test.tsx b/src/testing/experimental/__tests__/createTestSchema.test.tsx new file mode 100644 index 00000000000..99b579ddde2 --- /dev/null +++ b/src/testing/experimental/__tests__/createTestSchema.test.tsx @@ -0,0 +1,1259 @@ +import * as React from "react"; +import { + ApolloClient, + ApolloError, + InMemoryCache, + gql, +} from "../../../core/index.js"; +import type { TypedDocumentNode } from "../../../core/index.js"; +import { + Profiler, + createProfiler, + renderWithClient, + spyOnConsole, +} from "../../internal/index.js"; +import { createTestSchema } from "../createTestSchema.js"; +import { GraphQLError, buildSchema } from "graphql"; +import type { UseSuspenseQueryResult } from "../../../react/index.js"; +import { useMutation, useSuspenseQuery } from "../../../react/index.js"; +import userEvent from "@testing-library/user-event"; +import { act, screen } from "@testing-library/react"; +import { createSchemaFetch } from "../createSchemaFetch.js"; +import { + FallbackProps, + ErrorBoundary as ReactErrorBoundary, +} from "react-error-boundary"; +import { InvariantError } from "ts-invariant"; + +const typeDefs = /* GraphQL */ ` + type User { + id: ID! + age: Int! + name: String! + image: UserImage! + book: Book! + } + + type Author { + _id: ID! + name: String! + book: Book! + } + + union UserImage = UserImageSolidColor | UserImageURL + + type UserImageSolidColor { + color: String! + } + + type UserImageURL { + url: String! + } + + scalar Date + + interface Book { + id: ID! + title: String + publishedAt: Date + } + + type TextBook implements Book { + id: ID! + title: String + publishedAt: Date + text: String + } + + type ColoringBook implements Book { + id: ID! + title: String + publishedAt: Date + colors: [String] + } + + type Query { + viewer: User! + userById(id: ID!): User! + author: Author! + } + + type Mutation { + changeViewerName(newName: String!): User! + } +`; + +const schemaWithTypeDefs = buildSchema(typeDefs); + +const uri = "https://localhost:3000/graphql"; + +function createDefaultProfiler() { + return createProfiler({ + initialSnapshot: { + result: null as UseSuspenseQueryResult | null, + }, + }); +} + +function createErrorProfiler() { + return createProfiler({ + initialSnapshot: { + error: null as Error | null, + result: null as UseSuspenseQueryResult | null, + }, + }); +} + +function createTrackedErrorComponents( + Profiler: Profiler +) { + function ErrorFallback({ error }: FallbackProps) { + Profiler.mergeSnapshot({ error } as Partial); + + return
Error
; + } + + function ErrorBoundary({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); + } + + return { ErrorBoundary }; +} + +interface ViewerQueryData { + viewer: { + id: string; + name: string; + age: number; + book: { + id: string; + title: string; + publishedAt: string; + }; + }; +} + +describe("schema proxy", () => { + const schema = createTestSchema(schemaWithTypeDefs, { + resolvers: { + Query: { + viewer: () => ({ + name: "Jane Doe", + book: { + text: "Hello World", + title: "The Book", + }, + }), + }, + Book: { + __resolveType: (obj) => { + if ("text" in obj) { + return "TextBook"; + } + if ("colors" in obj) { + return "ColoringBook"; + } + throw new Error("Could not resolve type"); + }, + }, + }, + scalars: { + ID: () => "1", + Int: () => 42, + String: () => "String", + Date: () => new Date("January 1, 2024 01:00:00").toJSON().split("T")[0], + }, + }); + + it("mocks scalars and resolvers", async () => { + const Profiler = createDefaultProfiler(); + + using _fetch = createSchemaFetch(schema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + }); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Jane Doe", + book: { + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("allows schema forking with .fork", async () => { + const forkedSchema = schema.fork({ + resolvers: { + Query: { + viewer: () => ({ + book: { + colors: ["red", "blue", "green"], + title: "The Book", + }, + }), + }, + }, + }); + + const Profiler = createDefaultProfiler(); + + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query ViewerQuery { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + // In our resolvers defined in this test, we omit name so it uses + // the scalar default mock + name: "String", + book: { + // locally overrode the resolver for the book field + __typename: "ColoringBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("schema.fork does not pollute the original schema", async () => { + const Profiler = createDefaultProfiler(); + + schema.fork({ + resolvers: { + Query: { + viewer: () => ({ + book: { + colors: ["red", "blue", "green"], + title: "The Book", + }, + }), + }, + }, + }); + + using _fetch = createSchemaFetch(schema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Jane Doe", + book: { + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("allows you to call .fork without providing resolvers", async () => { + const forkedSchema = schema.fork(); + + forkedSchema.add({ + resolvers: { + Query: { + viewer: () => ({ + book: { + colors: ["red", "blue", "green"], + title: "The Book", + }, + }), + }, + }, + }); + + const Profiler = createDefaultProfiler(); + + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + // since we called .add and provided a new `viewer` resolver + // _without_ providing the viewer.name field in the response data, + // it renders with the default scalar mock for String + name: "String", + book: { + __typename: "ColoringBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("handles mutations", async () => { + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + let name = "Jane Doe"; + + const forkedSchema = schema.fork({ + resolvers: { + Query: { + viewer: () => ({ + book: { + text: "Hello World", + title: "The Book", + }, + }), + }, + User: { + name: () => name, + }, + Mutation: { + changeViewerName: (_: any, { newName }: { newName: string }) => { + name = newName; + return {}; + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const mutation = gql` + mutation { + changeViewerName(newName: "Alexandre") { + id + name + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + const [changeViewerName] = useMutation(mutation); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return ( +
+ + Hello +
+ ); + }; + + const user = userEvent.setup(); + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Jane Doe", + book: { + // locally overrode the resolver for the book field + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + + await act(() => user.click(screen.getByText("Change name"))); + + // initial suspended render + await Profiler.takeRender(); + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Alexandre", + book: { + // locally overrode the resolver for the book field + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("returns GraphQL errors", async () => { + using _consoleSpy = spyOnConsole("error"); + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + let name = "Jane Doe"; + + const forkedSchema = schema.fork({ + resolvers: { + Query: { + viewer: () => ({ + book: { + // text: "Hello World", <- this will cause a validation error + title: "The Book", + }, + }), + }, + User: { + name: () => name, + }, + }, + }); + + const Profiler = createErrorProfiler(); + + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.error).toEqual( + new ApolloError({ + graphQLErrors: [new GraphQLError("Could not resolve type")], + }) + ); + } + }); + + it("validates schema by default and returns validation errors", async () => { + using _consoleSpy = spyOnConsole("error"); + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + // invalid schema + const forkedSchema = { foo: "bar" }; + + const Profiler = createErrorProfiler(); + + const { ErrorBoundary } = createTrackedErrorComponents(Profiler); + + // @ts-expect-error - we're intentionally passing an invalid schema + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.error).toEqual( + new ApolloError({ + graphQLErrors: [ + new GraphQLError('Expected { foo: "bar" } to be a GraphQL schema.'), + ], + }) + ); + } + }); + + it("preserves resolvers from previous calls to .add on subsequent calls to .fork", async () => { + let name = "Virginia"; + + const schema = createTestSchema(schemaWithTypeDefs, { + resolvers: { + Query: { + viewer: () => ({ + name, + book: { + text: "Hello World", + title: "The Book", + }, + }), + }, + Book: { + __resolveType: (obj) => { + if ("text" in obj) { + return "TextBook"; + } + if ("colors" in obj) { + return "ColoringBook"; + } + throw new Error("Could not resolve type"); + }, + }, + }, + scalars: { + ID: () => "1", + Int: () => 42, + String: () => "String", + Date: () => new Date("January 1, 2024 01:00:00").toJSON().split("T")[0], + }, + }); + + schema.add({ + resolvers: { + Query: { + viewer: () => ({ + book: { + colors: ["red", "blue", "green"], + title: "A New Book", + }, + }), + }, + }, + }); + + schema.add({ + resolvers: { + User: { + name: () => name, + }, + }, + }); + + // should preserve resolvers from previous calls to .add + const forkedSchema = schema.fork({ + resolvers: { + Mutation: { + changeViewerName: (_: any, { newName }: { newName: string }) => { + name = newName; + return {}; + }, + }, + }, + }); + + const Profiler = createDefaultProfiler(); + + using _fetch = createSchemaFetch(forkedSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + ... on ColoringBook { + colors + } + } + } + } + `; + + const mutation = gql` + mutation { + changeViewerName(newName: "Alexandre") { + id + name + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + const [changeViewerName] = useMutation(mutation); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return ( +
+ + Hello +
+ ); + }; + + const user = userEvent.setup(); + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Virginia", + book: { + __typename: "ColoringBook", + colors: ["red", "blue", "green"], + id: "1", + publishedAt: "2024-01-01", + title: "A New Book", + }, + }, + }); + } + + await act(() => user.click(screen.getByText("Change name"))); + + await Profiler.takeRender(); + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Alexandre", + book: { + __typename: "ColoringBook", + colors: ["red", "blue", "green"], + id: "1", + publishedAt: "2024-01-01", + title: "A New Book", + }, + }, + }); + } + }); + + it("resets the schema with schema.reset()", async () => { + const resetTestSchema = createTestSchema(schema, { + resolvers: { + Query: { + viewer: () => ({ + book: { + text: "Hello World", + title: "Orlando: A Biography", + }, + }), + }, + Book: { + __resolveType: (obj) => { + if ("text" in obj) { + return "TextBook"; + } + if ("colors" in obj) { + return "ColoringBook"; + } + throw new Error("Could not resolve type"); + }, + }, + }, + }); + const Profiler = createDefaultProfiler(); + + resetTestSchema.add({ + resolvers: { + Query: { + viewer: () => ({ + book: { + text: "Hello World", + title: "The Waves", + }, + }), + }, + }, + }); + + using _fetch = createSchemaFetch(resetTestSchema).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + ... on ColoringBook { + colors + } + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return ( +
+ Hello +
+ ); + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "String", + book: { + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + // value set in this test with .add + title: "The Waves", + }, + }, + }); + } + + resetTestSchema.reset(); + + const user = userEvent.setup(); + + await act(() => user.click(screen.getByText("Refetch"))); + + // initial suspended render + await Profiler.takeRender(); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "String", + book: { + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + // original value + title: "Orlando: A Biography", + }, + }, + }); + } + }); + + it("createSchemaFetch respects min and max delay", async () => { + const Profiler = createDefaultProfiler(); + + const minDelay = 1500; + const maxDelay = 2000; + + using _fetch = createSchemaFetch(schema, { + delay: { min: minDelay, max: maxDelay }, + }).mockGlobal(); + + const client = new ApolloClient({ + cache: new InMemoryCache(), + uri, + }); + + const query: TypedDocumentNode = gql` + query { + viewer { + id + name + age + book { + id + title + publishedAt + } + } + } + `; + + const Fallback = () => { + return
Loading...
; + }; + + const App = () => { + return ( + }> + + + ); + }; + + const Child = () => { + const result = useSuspenseQuery(query); + + Profiler.mergeSnapshot({ + result, + } as Partial<{}>); + + return
Hello
; + }; + + renderWithClient(, { + client, + wrapper: Profiler, + }); + + // initial suspended render + await Profiler.takeRender(); + + await expect(Profiler).not.toRerender({ timeout: minDelay - 100 }); + + { + const { snapshot } = await Profiler.takeRender({ + // This timeout doesn't start until after our `minDelay - 100` + // timeout above, so we don't have to wait the full `maxDelay` + // here. + // Instead we can just wait for the difference between `maxDelay` + // and `minDelay`, plus a bit to prevent flakiness. + timeout: maxDelay - minDelay + 110, + }); + + expect(snapshot.result?.data).toEqual({ + viewer: { + __typename: "User", + age: 42, + id: "1", + name: "Jane Doe", + book: { + __typename: "TextBook", + id: "1", + publishedAt: "2024-01-01", + title: "The Book", + }, + }, + }); + } + }); + + it("should call invariant.error if min delay is greater than max delay", async () => { + await expect(async () => { + createSchemaFetch(schema, { + delay: { min: 3000, max: 1000 }, + }); + }).rejects.toThrow( + new InvariantError( + "Please configure a minimum delay that is less than the maximum delay. The default minimum delay is 3ms." + ) + ); + }); +}); diff --git a/src/testing/experimental/createSchemaFetch.ts b/src/testing/experimental/createSchemaFetch.ts new file mode 100644 index 00000000000..5c03ea0da0d --- /dev/null +++ b/src/testing/experimental/createSchemaFetch.ts @@ -0,0 +1,110 @@ +import { execute, validate } from "graphql"; +import type { GraphQLError, GraphQLSchema } from "graphql"; +import { ApolloError, gql } from "../../core/index.js"; +import { withCleanup } from "../internal/index.js"; +import { wait } from "../core/wait.js"; + +/** + * A function that accepts a static `schema` and a `mockFetchOpts` object and + * returns a disposable object with `mock` and `restore` functions. + * + * The `mock` function is a mock fetch function that is set on the global + * `window` object. This function intercepts any fetch requests and + * returns a response by executing the operation against the provided schema. + * + * The `restore` function is a cleanup function that will restore the previous + * `fetch`. It is automatically called if the function's return value is + * declared with `using`. If your environment does not support the language + * feature `using`, you should manually invoke the `restore` function. + * + * @param schema - A `GraphQLSchema`. + * @param mockFetchOpts - Configuration options. + * @returns An object with both `mock` and `restore` functions. + * + * @example + * ```js + * using _fetch = createSchemaFetch(schema); // automatically restores fetch after exiting the block + * + * const { restore } = createSchemaFetch(schema); + * restore(); // manually restore fetch if `using` is not supported + * ``` + * @since 3.10.0 + * @alpha + */ +const createSchemaFetch = ( + schema: GraphQLSchema, + mockFetchOpts: { + validate?: boolean; + delay?: { min: number; max: number }; + } = { validate: true } +) => { + const prevFetch = window.fetch; + const delayMin = mockFetchOpts.delay?.min ?? 3; + const delayMax = mockFetchOpts.delay?.max ?? delayMin + 2; + + if (delayMin > delayMax) { + throw new Error( + "Please configure a minimum delay that is less than the maximum delay. The default minimum delay is 3ms." + ); + } + + const mockFetch: (uri?: any, options?: any) => Promise = async ( + _uri, + options + ) => { + if (delayMin > 0) { + const randomDelay = Math.random() * (delayMax - delayMin) + delayMin; + await wait(randomDelay); + } + + const body = JSON.parse(options.body); + const document = gql(body.query); + + if (mockFetchOpts.validate) { + let validationErrors: readonly Error[] = []; + + try { + validationErrors = validate(schema, document); + } catch (e) { + validationErrors = [ + new ApolloError({ graphQLErrors: [e as GraphQLError] }), + ]; + } + + if (validationErrors?.length > 0) { + return new Response(JSON.stringify({ errors: validationErrors })); + } + } + + const result = await execute({ + schema, + document, + variableValues: body.variables, + operationName: body.operationName, + }); + + const stringifiedResult = JSON.stringify(result); + + return new Response(stringifiedResult); + }; + + function mockGlobal() { + window.fetch = mockFetch; + + const restore = () => { + if (window.fetch === mockFetch) { + window.fetch = prevFetch; + } + }; + + return withCleanup({ restore }, restore); + } + + return Object.assign(mockFetch, { + mockGlobal, + // if https://github.com/rbuckton/proposal-using-enforcement lands + // [Symbol.enter]: mockGlobal + }); +}; + +export { createSchemaFetch }; diff --git a/src/testing/experimental/createTestSchema.ts b/src/testing/experimental/createTestSchema.ts new file mode 100644 index 00000000000..5a4002c2902 --- /dev/null +++ b/src/testing/experimental/createTestSchema.ts @@ -0,0 +1,134 @@ +import type { GraphQLSchema } from "graphql"; +import { addResolversToSchema } from "@graphql-tools/schema"; +import { mergeResolvers } from "@graphql-tools/merge"; +import { createMockSchema } from "./graphql-tools/utils.js"; +import type { Resolvers } from "../../core/types.js"; + +type ProxiedSchema = GraphQLSchema & TestSchemaFns; + +interface TestSchemaFns { + add: (addOptions: { resolvers: Resolvers }) => ProxiedSchema; + fork: (forkOptions?: { resolvers?: Resolvers }) => ProxiedSchema; + reset: () => void; +} + +interface TestSchemaOptions { + resolvers: Resolvers; + scalars?: { [key: string]: any }; +} + +/** + * A function that creates a [Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) + * around a given `schema` with `resolvers`. This proxied schema can be used to + * progressively layer resolvers on top of the original schema using the `add` + * method. The `fork` method can be used to create a new proxied schema which + * can be modified independently of the original schema. `reset` will restore + * resolvers to the original proxied schema. + * + * @param schema - A `GraphQLSchema`. + * @param options - An `options` object that accepts `scalars` and `resolvers` objects. + * @returns A `ProxiedSchema` with `add`, `fork` and `reset` methods. + * + * @example + * ```js + * + * const schema = createTestSchema(schemaWithTypeDefs, { + * resolvers: { + Query: { + writer: () => ({ + name: "Ada Lovelace", + }), + } + }, + scalars: { + ID: () => "1", + Int: () => 36, + String: () => "String", + Date: () => new Date("December 10, 1815 01:00:00").toJSON().split("T")[0], + } + }); + * ``` + * @since 3.9.0 + * @alpha + */ +const createTestSchema = ( + schemaWithTypeDefs: GraphQLSchema, + options: TestSchemaOptions +): ProxiedSchema => { + let targetResolvers = { ...options.resolvers }; + let targetSchema = addResolversToSchema({ + schema: createMockSchema(schemaWithTypeDefs, options.scalars ?? {}), + resolvers: targetResolvers, + }); + + const fns: TestSchemaFns = { + add: ({ resolvers: newResolvers }) => { + // @ts-ignore TODO(fixme): IResolvers type does not play well with our Resolvers + targetResolvers = mergeResolvers([targetResolvers, newResolvers]); + + targetSchema = addResolversToSchema({ + schema: targetSchema, + resolvers: targetResolvers, + }); + + return targetSchema as ProxiedSchema; + }, + + fork: ({ resolvers: newResolvers } = {}) => { + return createTestSchema(targetSchema, { + // @ts-ignore TODO(fixme): IResolvers type does not play well with our Resolvers + resolvers: + mergeResolvers([targetResolvers, newResolvers]) ?? targetResolvers, + scalars: options.scalars, + }); + }, + + reset: () => { + targetSchema = addResolversToSchema({ + schema: schemaWithTypeDefs, + resolvers: options.resolvers, + }); + }, + }; + + const schema = new Proxy(targetSchema, { + get(_target, p) { + if (p in fns) { + return Reflect.get(fns, p); + } + + // An optimization that eliminates round-trips through the proxy + // on class methods invoked via `this` on a base class instance wrapped by + // the proxy. + // + // For example, consider the following class: + // + // class Base { + // foo(){ + // this.bar() + // } + // bar(){ + // ... + // } + // } + // + // Calling `proxy.foo()` would call `foo` with `this` being the proxy. + // This would result in calling `proxy.bar()` which would again invoke + // the proxy to resolve `bar` and call that method. + // + // Instead, calls to `proxy.foo()` should result in a call to + // `innerObject.foo()` with a `this` of `innerObject`, and that call + // should directly call `innerObject.bar()`. + + const property = Reflect.get(targetSchema, p); + if (typeof property === "function") { + return property.bind(targetSchema); + } + return property; + }, + }); + + return schema as ProxiedSchema; +}; + +export { createTestSchema }; diff --git a/src/testing/experimental/graphql-tools/LICENSE b/src/testing/experimental/graphql-tools/LICENSE new file mode 100644 index 00000000000..f5940526b77 --- /dev/null +++ b/src/testing/experimental/graphql-tools/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 The Guild, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/testing/experimental/graphql-tools/utils.test.ts b/src/testing/experimental/graphql-tools/utils.test.ts new file mode 100644 index 00000000000..0d7a9c63fac --- /dev/null +++ b/src/testing/experimental/graphql-tools/utils.test.ts @@ -0,0 +1,227 @@ +// Originally from @graphql-tools/mock +// https://github.com/ardatan/graphql-tools/blob/4b56b04d69b02919f6c5fa4f97d33da63f36e8c8/packages/mock/tests/addMocksToSchema.spec.ts + +import { buildSchema, graphql } from "graphql"; +import { createMockSchema } from "./utils.js"; + +const mockDate = new Date().toJSON().split("T")[0]; + +const mocks = { + Int: () => 6, + Float: () => 22.1, + String: () => "string", + ID: () => "id", + Date: () => mockDate, +}; + +const typeDefs = /* GraphQL */ ` + type User { + id: ID! + age: Int! + name: String! + image: UserImage! + book: Book! + } + + type Author { + _id: ID! + name: String! + book: Book! + } + + union UserImage = UserImageSolidColor | UserImageURL + + type UserImageSolidColor { + color: String! + } + + type UserImageURL { + url: String! + } + + scalar Date + + interface Book { + id: ID! + title: String + publishedAt: Date + } + + type TextBook implements Book { + id: ID! + title: String + publishedAt: Date + text: String + } + + type ColoringBook implements Book { + id: ID! + title: String + publishedAt: Date + colors: [String] + } + + type Query { + viewer: User! + userById(id: ID!): User! + author: Author! + } + + type Mutation { + changeViewerName(newName: String!): User! + } +`; + +const schema = buildSchema(typeDefs); + +describe("addMocksToSchema", () => { + it("basic", async () => { + const query = /* GraphQL */ ` + query { + viewer { + id + name + age + } + } + `; + + const mockedSchema = createMockSchema(schema, mocks); + + const { data, errors } = await graphql({ + schema: mockedSchema, + source: query, + }); + + expect(errors).not.toBeDefined(); + expect(data).toBeDefined(); + + const viewerData = data?.["viewer"] as any; + expect(typeof viewerData["id"]).toBe("string"); + expect(typeof viewerData["name"]).toBe("string"); + expect(typeof viewerData["age"]).toBe("number"); + + const { data: data2 } = await graphql({ + schema: mockedSchema, + source: query, + }); + + const viewerData2 = data2?.["viewer"] as any; + + expect(viewerData2["id"]).toEqual(viewerData["id"]); + }); + + it("handle _id key field", async () => { + const query = /* GraphQL */ ` + query { + author { + _id + name + } + } + `; + const mockedSchema = createMockSchema(schema, mocks); + const { data, errors } = await graphql({ + schema: mockedSchema, + source: query, + }); + + expect(errors).not.toBeDefined(); + expect(data).toBeDefined(); + const viewerData = data?.["author"] as any; + expect(typeof viewerData["_id"]).toBe("string"); + expect(typeof viewerData["name"]).toBe("string"); + + const { data: data2 } = await graphql({ + schema: mockedSchema, + source: query, + }); + + const viewerData2 = data2?.["author"] as any; + + expect(viewerData2["_id"]).toEqual(viewerData["_id"]); + }); + + it("should handle union type", async () => { + const query = /* GraphQL */ ` + query { + viewer { + image { + __typename + ... on UserImageURL { + url + } + ... on UserImageSolidColor { + color + } + } + } + } + `; + + const mockedSchema = createMockSchema(schema, mocks); + + const { data, errors } = await graphql({ + schema: mockedSchema, + source: query, + }); + + expect(errors).not.toBeDefined(); + expect(data).toBeDefined(); + expect((data!["viewer"] as any)["image"]["__typename"]).toBeDefined(); + }); + + it("should handle interface type", async () => { + const query = /* GraphQL */ ` + query { + viewer { + book { + title + __typename + ... on TextBook { + text + } + ... on ColoringBook { + colors + } + } + } + } + `; + + const mockedSchema = createMockSchema(schema, mocks); + + const { data, errors } = await graphql({ + schema: mockedSchema, + source: query, + }); + + expect(errors).not.toBeDefined(); + expect(data).toBeDefined(); + expect((data!["viewer"] as any)["book"]["__typename"]).toBeDefined(); + }); + + it("should handle custom scalars", async () => { + const query = /* GraphQL */ ` + query { + viewer { + book { + title + publishedAt + } + } + } + `; + + const mockedSchema = createMockSchema(schema, mocks); + + const { data, errors } = await graphql({ + schema: mockedSchema, + source: query, + }); + + expect(errors).not.toBeDefined(); + expect(data).toBeDefined(); + expect((data!["viewer"] as any)["book"]["publishedAt"]).toBe(mockDate); + }); +}); diff --git a/src/testing/experimental/graphql-tools/utils.ts b/src/testing/experimental/graphql-tools/utils.ts new file mode 100644 index 00000000000..4ff08d7ab0f --- /dev/null +++ b/src/testing/experimental/graphql-tools/utils.ts @@ -0,0 +1,251 @@ +import type { + GraphQLFieldResolver, + GraphQLObjectType, + GraphQLOutputType, + GraphQLSchema, +} from "graphql"; + +import { + GraphQLInterfaceType, + GraphQLString, + GraphQLUnionType, + defaultFieldResolver, + getNullableType, + isAbstractType, + isEnumType, + isInterfaceType, + isListType, + isObjectType, + isScalarType, + isUnionType, +} from "graphql"; + +import { isNonNullObject } from "../../../utilities/index.js"; +import { MapperKind, mapSchema, getRootTypeNames } from "@graphql-tools/utils"; + +// Taken from @graphql-tools/mock: +// https://github.com/ardatan/graphql-tools/blob/4b56b04d69b02919f6c5fa4f97d33da63f36e8c8/packages/mock/src/utils.ts#L20 +const takeRandom = (arr: T[]) => arr[Math.floor(Math.random() * arr.length)]; + +/** + * A function that accepts a static `schema` and a `mocks` object for specifying + * default scalar mocks and returns a `GraphQLSchema`. + * + * @param staticSchema - A static `GraphQLSchema`. + * @param mocks - An object containing scalar mocks. + * @returns A `GraphQLSchema` with scalar mocks. + * + * @example + * ```js + * const mockedSchema = createMockSchema(schema, { + ID: () => "1", + Int: () => 42, + String: () => "String", + Date: () => new Date("January 1, 2024 01:00:00").toJSON().split("T")[0], + }); + * ``` + * @since 3.10.0 + * @alpha + */ +const createMockSchema = ( + staticSchema: GraphQLSchema, + mocks: { [key: string]: any } +) => { + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/MockStore.ts#L613 + const getType = (typeName: string) => { + const type = staticSchema.getType(typeName); + + if (!type || !(isObjectType(type) || isInterfaceType(type))) { + throw new Error( + `${typeName} does not exist on schema or is not an object or interface` + ); + } + + return type; + }; + + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/MockStore.ts#L597 + const getFieldType = (typeName: string, fieldName: string) => { + if (fieldName === "__typename") { + return GraphQLString; + } + + const type = getType(typeName); + + const field = type.getFields()[fieldName]; + + if (!field) { + throw new Error(`${fieldName} does not exist on type ${typeName}`); + } + + return field.type; + }; + + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/MockStore.ts#L527 + const generateValueFromType = (fieldType: GraphQLOutputType): unknown => { + const nullableType = getNullableType(fieldType); + + if (isScalarType(nullableType)) { + const mockFn = mocks[nullableType.name]; + + if (typeof mockFn !== "function") { + throw new Error(`No mock defined for type "${nullableType.name}"`); + } + + return mockFn(); + } else if (isEnumType(nullableType)) { + const mockFn = mocks[nullableType.name]; + + if (typeof mockFn === "function") return mockFn(); + + const values = nullableType.getValues().map((v) => v.value); + + return takeRandom(values); + } else if (isObjectType(nullableType)) { + return {}; + } else if (isListType(nullableType)) { + return [...new Array(2)].map(() => + generateValueFromType(nullableType.ofType) + ); + } else if (isAbstractType(nullableType)) { + const mock = mocks[nullableType.name]; + + let typeName: string; + + let values: { [key: string]: unknown } = {}; + + if (!mock) { + typeName = takeRandom( + staticSchema.getPossibleTypes(nullableType).map((t) => t.name) + ); + } else if (typeof mock === "function") { + const mockRes = mock(); + + if (mockRes === null) return null; + + if (!isNonNullObject(mockRes)) { + throw new Error( + `Value returned by the mock for ${nullableType.name} is not an object or null` + ); + } + + values = mockRes; + + if (typeof values["__typename"] !== "string") { + throw new Error( + `Please return a __typename in "${nullableType.name}"` + ); + } + + typeName = values["__typename"]; + } else if ( + isNonNullObject(mock) && + typeof mock["__typename"] === "function" + ) { + const mockRes = mock["__typename"](); + + if (typeof mockRes !== "string") { + throw new Error( + `'__typename' returned by the mock for abstract type ${nullableType.name} is not a string` + ); + } + + typeName = mockRes; + } else { + throw new Error(`Please return a __typename in "${nullableType.name}"`); + } + + return typeName; + } else { + throw new Error(`${nullableType} not implemented`); + } + }; + + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/utils.ts#L53 + const isRootType = (type: GraphQLObjectType, schema: GraphQLSchema) => { + const rootTypeNames = getRootTypeNames(schema); + + return rootTypeNames.has(type.name); + }; + + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/addMocksToSchema.ts#L123 + const mockResolver: GraphQLFieldResolver = ( + source, + args, + contex, + info + ) => { + const defaultResolvedValue = defaultFieldResolver( + source, + args, + contex, + info + ); + + // priority to default resolved value + if (defaultResolvedValue !== undefined) return defaultResolvedValue; + + // we have to handle the root mutation, root query and root subscription types + // differently, because no resolver is called at the root + if (isRootType(info.parentType, info.schema)) { + return { + typeName: info.parentType.name, + key: "ROOT", + fieldName: info.fieldName, + fieldArgs: args, + }; + } + + if (defaultResolvedValue === undefined) { + const fieldType = getFieldType(info.parentType.name, info.fieldName); + + return generateValueFromType(fieldType); + } + + return undefined; + }; + + // Taken from @graphql-tools/mock: + // https://github.com/ardatan/graphql-tools/blob/5ed60e44f94868f976cd28fe1b6a764fb146bbe9/packages/mock/src/addMocksToSchema.ts#L176 + return mapSchema(staticSchema, { + [MapperKind.OBJECT_FIELD]: (fieldConfig) => { + const newFieldConfig = { ...fieldConfig }; + + const oldResolver = fieldConfig.resolve; + + if (!oldResolver) { + newFieldConfig.resolve = mockResolver; + } + return newFieldConfig; + }, + + [MapperKind.ABSTRACT_TYPE]: (type) => { + if (type.resolveType != null && type.resolveType.length) { + return; + } + + const typeResolver = (typename: string) => { + return typename; + }; + + if (isUnionType(type)) { + return new GraphQLUnionType({ + ...type.toConfig(), + resolveType: typeResolver, + }); + } else { + return new GraphQLInterfaceType({ + ...type.toConfig(), + resolveType: typeResolver, + }); + } + }, + }); +}; + +export { createMockSchema }; diff --git a/src/testing/experimental/index.ts b/src/testing/experimental/index.ts new file mode 100644 index 00000000000..a7080de66d2 --- /dev/null +++ b/src/testing/experimental/index.ts @@ -0,0 +1,2 @@ +export { createTestSchema } from "./createTestSchema.js"; +export { createSchemaFetch } from "./createSchemaFetch.js"; diff --git a/src/testing/internal/ObservableStream.ts b/src/testing/internal/ObservableStream.ts index f0416692331..b19be0d469b 100644 --- a/src/testing/internal/ObservableStream.ts +++ b/src/testing/internal/ObservableStream.ts @@ -29,6 +29,7 @@ async function* observableToAsyncEventIterator(observable: Observable) { (error) => resolveNext({ type: "error", error }), () => resolveNext({ type: "complete" }) ); + yield "initialization value" as unknown as Promise>; while (true) { yield promises.shift()!; @@ -54,7 +55,11 @@ class IteratorStream { export class ObservableStream extends IteratorStream> { constructor(observable: Observable) { - super(observableToAsyncEventIterator(observable)); + const iterator = observableToAsyncEventIterator(observable); + // we need to call next() once to start the generator so we immediately subscribe. + // the first value is always "initialization value" which we don't care about + iterator.next(); + super(iterator); } async takeNext(options?: TakeOptions): Promise { diff --git a/src/testing/internal/disposables/disableActWarnings.ts b/src/testing/internal/disposables/disableActWarnings.ts new file mode 100644 index 00000000000..c5254c8dc1d --- /dev/null +++ b/src/testing/internal/disposables/disableActWarnings.ts @@ -0,0 +1,15 @@ +import { withCleanup } from "./withCleanup.js"; + +/** + * Temporarily disable act warnings. + * + * https://github.com/reactwg/react-18/discussions/102 + */ +export function disableActWarnings() { + const prev = { prevActEnv: (globalThis as any).IS_REACT_ACT_ENVIRONMENT }; + (globalThis as any).IS_REACT_ACT_ENVIRONMENT = false; + + return withCleanup(prev, ({ prevActEnv }) => { + (globalThis as any).IS_REACT_ACT_ENVIRONMENT = prevActEnv; + }); +} diff --git a/src/testing/internal/disposables/index.ts b/src/testing/internal/disposables/index.ts index 6d232565db4..9895d129589 100644 --- a/src/testing/internal/disposables/index.ts +++ b/src/testing/internal/disposables/index.ts @@ -1,2 +1,3 @@ +export { disableActWarnings } from "./disableActWarnings.js"; export { spyOnConsole } from "./spyOnConsole.js"; export { withCleanup } from "./withCleanup.js"; diff --git a/src/testing/internal/index.ts b/src/testing/internal/index.ts index 73a9a00ff0e..9f8b3faae9a 100644 --- a/src/testing/internal/index.ts +++ b/src/testing/internal/index.ts @@ -1,3 +1,22 @@ export * from "./profile/index.js"; export * from "./disposables/index.js"; export { ObservableStream } from "./ObservableStream.js"; + +export type { + SimpleCaseData, + PaginatedCaseData, + PaginatedCaseVariables, + VariablesCaseData, + VariablesCaseVariables, +} from "./scenarios/index.js"; +export { + setupSimpleCase, + setupVariablesCase, + setupPaginatedCase, +} from "./scenarios/index.js"; + +export type { + RenderWithClientOptions, + RenderWithMocksOptions, +} from "./renderHelpers.js"; +export { renderWithClient, renderWithMocks } from "./renderHelpers.js"; diff --git a/src/testing/internal/profile/Render.tsx b/src/testing/internal/profile/Render.tsx index 24b4737c2c0..284594c9f51 100644 --- a/src/testing/internal/profile/Render.tsx +++ b/src/testing/internal/profile/Render.tsx @@ -30,12 +30,12 @@ export interface BaseRender { type Screen = typeof screen; /** @internal */ export type SyncScreen = { - [K in keyof Screen]: K extends `find${string}` - ? { - /** @deprecated A snapshot is static, so avoid async queries! */ - (...args: Parameters): ReturnType; - } - : Screen[K]; + [K in keyof Screen]: K extends `find${string}` ? + { + /** @deprecated A snapshot is static, so avoid async queries! */ + (...args: Parameters): ReturnType; + } + : Screen[K]; }; /** @internal */ @@ -62,6 +62,8 @@ export interface Render extends BaseRender { * ``` */ withinDOM: () => SyncScreen; + + renderedComponents: Array; } /** @internal */ @@ -77,7 +79,8 @@ export class RenderInstance implements Render { constructor( baseRender: BaseRender, public snapshot: Snapshot, - private stringifiedDOM: string | undefined + private stringifiedDOM: string | undefined, + public renderedComponents: Array ) { this.id = baseRender.id; this.phase = baseRender.phase; @@ -121,7 +124,7 @@ export class RenderInstance implements Render { return (this._domSnapshot = body); } - get withinDOM() { + get withinDOM(): () => SyncScreen { const snapScreen = Object.assign(within(this.domSnapshot), { debug: ( ...[dom = this.domSnapshot, ...rest]: Parameters diff --git a/src/testing/internal/profile/context.tsx b/src/testing/internal/profile/context.tsx new file mode 100644 index 00000000000..a8488e73a6c --- /dev/null +++ b/src/testing/internal/profile/context.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; + +export interface ProfilerContextValue { + renderedComponents: Array; +} + +const ProfilerContext = React.createContext( + undefined +); + +export function ProfilerContextProvider({ + children, + value, +}: { + children: React.ReactNode; + value: ProfilerContextValue; +}) { + const parentContext = useProfilerContext(); + + if (parentContext) { + throw new Error("Profilers should not be nested in the same tree"); + } + + return ( + + {children} + + ); +} + +export function useProfilerContext() { + return React.useContext(ProfilerContext); +} diff --git a/src/testing/internal/profile/index.ts b/src/testing/internal/profile/index.ts index 01bb526c52c..3d9ddd55559 100644 --- a/src/testing/internal/profile/index.ts +++ b/src/testing/internal/profile/index.ts @@ -1,8 +1,15 @@ export type { NextRenderOptions, + Profiler, ProfiledComponent, ProfiledHook, } from "./profile.js"; -export { profile, profileHook, WaitForRenderTimeoutError } from "./profile.js"; +export { + createProfiler, + profile, + profileHook, + useTrackRenders, + WaitForRenderTimeoutError, +} from "./profile.js"; export type { SyncScreen } from "./Render.js"; diff --git a/src/testing/internal/profile/profile.tsx b/src/testing/internal/profile/profile.tsx index 12527844566..d1c7731ec2a 100644 --- a/src/testing/internal/profile/profile.tsx +++ b/src/testing/internal/profile/profile.tsx @@ -8,6 +8,9 @@ global.TextDecoder ??= TextDecoder; import type { Render, BaseRender } from "./Render.js"; import { RenderInstance } from "./Render.js"; import { applyStackTrace, captureStackTrace } from "./traces.js"; +import type { ProfilerContextValue } from "./context.js"; +import { ProfilerContextProvider, useProfilerContext } from "./context.js"; +import { disableActWarnings } from "../disposables/index.js"; type ValidSnapshot = void | (object & { /* not a function */ call?: never }); @@ -20,20 +23,37 @@ export interface NextRenderOptions { } /** @internal */ -export interface ProfiledComponent - extends React.FC, - ProfiledComponentFields, - ProfiledComponenOnlyFields {} +interface ProfilerProps { + children: React.ReactNode; +} + +/** @internal */ +export interface Profiler + extends React.FC, + ProfiledComponentFields, + ProfiledComponentOnlyFields {} -interface UpdateSnapshot { +interface ReplaceSnapshot { (newSnapshot: Snapshot): void; (updateSnapshot: (lastSnapshot: Readonly) => Snapshot): void; } -interface ProfiledComponenOnlyFields { - updateSnapshot: UpdateSnapshot; +interface MergeSnapshot { + (partialSnapshot: Partial): void; + ( + updatePartialSnapshot: ( + lastSnapshot: Readonly + ) => Partial + ): void; } -interface ProfiledComponentFields { + +interface ProfiledComponentOnlyFields { + // Allows for partial updating of the snapshot by shallow merging the results + mergeSnapshot: MergeSnapshot; + // Performs a full replacement of the snapshot + replaceSnapshot: ReplaceSnapshot; +} +interface ProfiledComponentFields { /** * An array of all renders that have happened so far. * Errors thrown during component render will be captured here, too. @@ -54,21 +74,14 @@ interface ProfiledComponentFields { */ takeRender(options?: NextRenderOptions): Promise>; /** - * Returns the current render count. + * Returns the total number of renders. */ - currentRenderCount(): number; + totalRenderCount(): number; /** * Returns the current render. * @throws {Error} if no render has happened yet */ getCurrentRender(): Render; - /** - * Iterates the renders until the render count is reached. - */ - takeUntilRenderCount( - count: number, - optionsPerRender?: NextRenderOptions - ): Promise; /** * Waits for the next render to happen. * Does not advance the render iterator. @@ -76,32 +89,73 @@ interface ProfiledComponentFields { waitForNextRender(options?: NextRenderOptions): Promise>; } +export interface ProfiledComponent + extends React.FC, + ProfiledComponentFields, + ProfiledComponentOnlyFields {} + /** @internal */ -export function profile< - Snapshot extends ValidSnapshot = void, - Props = Record, ->({ +export function profile({ Component, + ...options +}: Parameters>[0] & { + Component: React.ComponentType; +}): ProfiledComponent { + const Profiler = createProfiler(options); + + return Object.assign( + function ProfiledComponent(props: Props) { + return ( + + + + ); + }, + { + mergeSnapshot: Profiler.mergeSnapshot, + replaceSnapshot: Profiler.replaceSnapshot, + getCurrentRender: Profiler.getCurrentRender, + peekRender: Profiler.peekRender, + takeRender: Profiler.takeRender, + totalRenderCount: Profiler.totalRenderCount, + waitForNextRender: Profiler.waitForNextRender, + get renders() { + return Profiler.renders; + }, + } + ); +} + +/** @internal */ +export function createProfiler({ onRender, snapshotDOM = false, initialSnapshot, + skipNonTrackingRenders, }: { - Component: React.ComponentType; onRender?: ( info: BaseRender & { snapshot: Snapshot; - updateSnapshot: UpdateSnapshot; + replaceSnapshot: ReplaceSnapshot; + mergeSnapshot: MergeSnapshot; } ) => void; snapshotDOM?: boolean; initialSnapshot?: Snapshot; -}) { - let currentRender: Render | undefined; + /** + * This will skip renders during which no renders tracked by + * `useTrackRenders` occured. + */ + skipNonTrackingRenders?: boolean; +} = {}) { let nextRender: Promise> | undefined; let resolveNextRender: ((render: Render) => void) | undefined; let rejectNextRender: ((error: unknown) => void) | undefined; + function resetNextRender() { + nextRender = resolveNextRender = rejectNextRender = undefined; + } const snapshotRef = { current: initialSnapshot }; - const updateSnapshot: UpdateSnapshot = (snap) => { + const replaceSnapshot: ReplaceSnapshot = (snap) => { if (typeof snap === "function") { if (!initialSnapshot) { throw new Error( @@ -109,15 +163,29 @@ export function profile< ); } snapshotRef.current = snap( - typeof snapshotRef.current === "object" - ? // "cheap best effort" to prevent accidental mutation of the last snapshot - { ...snapshotRef.current! } - : snapshotRef.current! + typeof snapshotRef.current === "object" ? + // "cheap best effort" to prevent accidental mutation of the last snapshot + { ...snapshotRef.current! } + : snapshotRef.current! ); } else { snapshotRef.current = snap; } }; + + const mergeSnapshot: MergeSnapshot = (partialSnapshot) => { + replaceSnapshot((snapshot) => ({ + ...snapshot, + ...(typeof partialSnapshot === "function" ? + partialSnapshot(snapshot) + : partialSnapshot), + })); + }; + + const profilerContext: ProfilerContextValue = { + renderedComponents: [], + }; + const profilerOnRender: React.ProfilerOnRenderCallback = ( id, phase, @@ -126,6 +194,12 @@ export function profile< startTime, commitTime ) => { + if ( + skipNonTrackingRenders && + profilerContext.renderedComponents.length === 0 + ) { + return; + } const baseRender = { id, phase, @@ -133,7 +207,7 @@ export function profile< baseDuration, startTime, commitTime, - count: Profiled.renders.length + 1, + count: Profiler.renders.length + 1, }; try { /* @@ -145,68 +219,84 @@ export function profile< */ onRender?.({ ...baseRender, - updateSnapshot, + replaceSnapshot, + mergeSnapshot, snapshot: snapshotRef.current!, }); const snapshot = snapshotRef.current as Snapshot; - const domSnapshot = snapshotDOM - ? window.document.body.innerHTML - : undefined; - const render = new RenderInstance(baseRender, snapshot, domSnapshot); - // eslint-disable-next-line testing-library/render-result-naming-convention - currentRender = render; - Profiled.renders.push(render); + const domSnapshot = + snapshotDOM ? window.document.body.innerHTML : undefined; + const render = new RenderInstance( + baseRender, + snapshot, + domSnapshot, + profilerContext.renderedComponents + ); + profilerContext.renderedComponents = []; + Profiler.renders.push(render); resolveNextRender?.(render); } catch (error) { - Profiled.renders.push({ + Profiler.renders.push({ phase: "snapshotError", - count: Profiled.renders.length, + count: Profiler.renders.length, error, }); rejectNextRender?.(error); } finally { - nextRender = resolveNextRender = rejectNextRender = undefined; + resetNextRender(); } }; let iteratorPosition = 0; - const Profiled: ProfiledComponent = Object.assign( - (props: Props) => ( - - - - ), + const Profiler: Profiler = Object.assign( + ({ children }: ProfilerProps) => { + return ( + + + {children} + + + ); + }, { - updateSnapshot, - } satisfies ProfiledComponenOnlyFields, + replaceSnapshot, + mergeSnapshot, + } satisfies ProfiledComponentOnlyFields, { renders: new Array< | Render | { phase: "snapshotError"; count: number; error: unknown } >(), - currentRenderCount() { - return Profiled.renders.length; + totalRenderCount() { + return Profiler.renders.length; }, async peekRender(options: NextRenderOptions = {}) { - if (iteratorPosition < Profiled.renders.length) { - const render = Profiled.renders[iteratorPosition]; + if (iteratorPosition < Profiler.renders.length) { + const render = Profiler.renders[iteratorPosition]; + if (render.phase === "snapshotError") { throw render.error; } + return render; } - const render = Profiled.waitForNextRender({ - [_stackTrace]: captureStackTrace(Profiled.peekRender), + return Profiler.waitForNextRender({ + [_stackTrace]: captureStackTrace(Profiler.peekRender), ...options, }); - return render; }, async takeRender(options: NextRenderOptions = {}) { - let error: { message?: string } | undefined = undefined; + // In many cases we do not control the resolution of the suspended + // promise which results in noisy tests when the profiler due to + // repeated act warnings. + using _disabledActWarnings = disableActWarnings(); + + let error: unknown = undefined; + try { - return await Profiled.peekRender({ - [_stackTrace]: captureStackTrace(Profiled.takeRender), + return await Profiler.peekRender({ + [_stackTrace]: captureStackTrace(Profiler.takeRender), ...options, }); } catch (e) { @@ -219,24 +309,31 @@ export function profile< } }, getCurrentRender() { - if (!currentRender) { - throw new Error("Has not been rendered yet!"); + // The "current" render should point at the same render that the most + // recent `takeRender` call returned, so we need to get the "previous" + // iterator position, otherwise `takeRender` advances the iterator + // to the next render. This means we need to call `takeRender` at least + // once before we can get a current render. + const currentPosition = iteratorPosition - 1; + + if (currentPosition < 0) { + throw new Error( + "No current render available. You need to call `takeRender` before you can get the current render." + ); } - return currentRender; - }, - async takeUntilRenderCount( - count: number, - optionsPerRender?: NextRenderOptions - ) { - while (Profiled.renders.length < count) { - await Profiled.takeRender(optionsPerRender); + + const render = Profiler.renders[currentPosition]; + + if (render.phase === "snapshotError") { + throw render.error; } + return render; }, waitForNextRender({ timeout = 1000, // capture the stack trace here so its stack trace is as close to the calling code as possible [_stackTrace]: stackTrace = captureStackTrace( - Profiled.waitForNextRender + Profiler.waitForNextRender ), }: NextRenderOptions = {}) { if (!nextRender) { @@ -246,21 +343,20 @@ export function profile< rejectNextRender = reject; }), new Promise>((_, reject) => - setTimeout( - () => - reject( - applyStackTrace(new WaitForRenderTimeoutError(), stackTrace) - ), - timeout - ) + setTimeout(() => { + reject( + applyStackTrace(new WaitForRenderTimeoutError(), stackTrace) + ); + resetNextRender(); + }, timeout) ), ]); } return nextRender; }, - } satisfies ProfiledComponentFields + } satisfies ProfiledComponentFields ); - return Profiled; + return Profiler; } /** @internal */ @@ -274,19 +370,16 @@ export class WaitForRenderTimeoutError extends Error { type StringReplaceRenderWithSnapshot = T extends `${infer Pre}Render${infer Post}` ? `${Pre}Snapshot${Post}` : T; -type ResultReplaceRenderWithSnapshot = T extends ( - ...args: infer Args -) => Render - ? (...args: Args) => Snapshot - : T extends (...args: infer Args) => Promise> - ? (...args: Args) => Promise +type ResultReplaceRenderWithSnapshot = + T extends (...args: infer Args) => Render ? + (...args: Args) => Snapshot + : T extends (...args: infer Args) => Promise> ? + (...args: Args) => Promise : T; -type ProfiledHookFields = ProfiledComponentFields< - Props, - ReturnValue -> extends infer PC - ? { +type ProfiledHookFields = + ProfiledComponentFields extends infer PC ? + { [K in keyof PC as StringReplaceRenderWithSnapshot< K & string >]: ResultReplaceRenderWithSnapshot; @@ -296,46 +389,81 @@ type ProfiledHookFields = ProfiledComponentFields< /** @internal */ export interface ProfiledHook extends React.FC, - ProfiledHookFields { - ProfiledComponent: ProfiledComponent; + ProfiledHookFields { + Profiler: Profiler; } /** @internal */ export function profileHook( renderCallback: (props: Props) => ReturnValue ): ProfiledHook { - let returnValue: ReturnValue; - const Component = (props: Props) => { - ProfiledComponent.updateSnapshot(renderCallback(props)); + const Profiler = createProfiler(); + + const ProfiledHook = (props: Props) => { + Profiler.replaceSnapshot(renderCallback(props)); return null; }; - const ProfiledComponent = profile({ - Component, - onRender: () => returnValue, - }); + return Object.assign( - function ProfiledHook(props: Props) { - return ; + function App(props: Props) { + return ( + + + + ); }, { - ProfiledComponent, + Profiler, }, { - renders: ProfiledComponent.renders, - currentSnapshotCount: ProfiledComponent.currentRenderCount, + renders: Profiler.renders, + totalSnapshotCount: Profiler.totalRenderCount, async peekSnapshot(options) { - return (await ProfiledComponent.peekRender(options)).snapshot; + return (await Profiler.peekRender(options)).snapshot; }, async takeSnapshot(options) { - return (await ProfiledComponent.takeRender(options)).snapshot; + return (await Profiler.takeRender(options)).snapshot; }, getCurrentSnapshot() { - return ProfiledComponent.getCurrentRender().snapshot; + return Profiler.getCurrentRender().snapshot; }, - takeUntilSnapshotCount: ProfiledComponent.takeUntilRenderCount, async waitForNextSnapshot(options) { - return (await ProfiledComponent.waitForNextRender(options)).snapshot; + return (await Profiler.waitForNextRender(options)).snapshot; }, - } satisfies ProfiledHookFields + } satisfies ProfiledHookFields ); } + +function resolveR18HookOwner(): React.ComponentType | undefined { + return (React as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + ?.ReactCurrentOwner?.current?.elementType; +} + +function resolveR19HookOwner(): React.ComponentType | undefined { + return ( + React as any + ).__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE?.A?.getOwner() + .elementType; +} + +export function useTrackRenders({ name }: { name?: string } = {}) { + const component = name || resolveR18HookOwner() || resolveR19HookOwner(); + + if (!component) { + throw new Error( + "useTrackRender: Unable to determine component. Please ensure the hook is called inside a rendered component or provide a `name` option." + ); + } + + const ctx = useProfilerContext(); + + if (!ctx) { + throw new Error( + "useTrackComponentRender: A Profiler must be created and rendered to track component renders" + ); + } + + React.useLayoutEffect(() => { + ctx.renderedComponents.unshift(component); + }); +} diff --git a/src/testing/internal/profile/traces.ts b/src/testing/internal/profile/traces.ts index 5e998d6dbab..9dfbc59e82a 100644 --- a/src/testing/internal/profile/traces.ts +++ b/src/testing/internal/profile/traces.ts @@ -11,11 +11,9 @@ export function captureStackTrace(callingFunction?: string | (() => {})) { } const callerName = - typeof callingFunction === "string" - ? callingFunction - : callingFunction - ? callingFunction.name - : undefined; + typeof callingFunction === "string" ? callingFunction + : callingFunction ? callingFunction.name + : undefined; if (callerName && stack.includes(callerName)) { const lines = stack.split("\n"); diff --git a/src/testing/internal/renderHelpers.tsx b/src/testing/internal/renderHelpers.tsx new file mode 100644 index 00000000000..c47a533d09c --- /dev/null +++ b/src/testing/internal/renderHelpers.tsx @@ -0,0 +1,70 @@ +import * as React from "react"; +import type { ReactElement } from "react"; +import { render } from "@testing-library/react"; +import type { Queries, RenderOptions, queries } from "@testing-library/react"; +import type { ApolloClient } from "../../core/index.js"; +import { ApolloProvider } from "../../react/index.js"; +import type { MockedProviderProps } from "../react/MockedProvider.js"; +import { MockedProvider } from "../react/MockedProvider.js"; + +export interface RenderWithClientOptions< + Q extends Queries = typeof queries, + Container extends Element | DocumentFragment = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +> extends RenderOptions { + client: ApolloClient; +} + +export function renderWithClient< + Q extends Queries = typeof queries, + Container extends Element | DocumentFragment = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +>( + ui: ReactElement, + { + client, + wrapper: Wrapper = React.Fragment, + ...renderOptions + }: RenderWithClientOptions +) { + return render(ui, { + ...renderOptions, + wrapper: ({ children }) => { + return ( + + {children} + + ); + }, + }); +} + +export interface RenderWithMocksOptions< + Q extends Queries = typeof queries, + Container extends Element | DocumentFragment = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +> extends RenderOptions, + MockedProviderProps {} + +export function renderWithMocks< + Q extends Queries = typeof queries, + Container extends Element | DocumentFragment = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +>( + ui: ReactElement, + { + wrapper: Wrapper = React.Fragment, + ...renderOptions + }: RenderWithMocksOptions +) { + return render(ui, { + ...renderOptions, + wrapper: ({ children }) => { + return ( + + {children} + + ); + }, + }); +} diff --git a/src/testing/internal/scenarios/index.ts b/src/testing/internal/scenarios/index.ts new file mode 100644 index 00000000000..2ed2bb5c523 --- /dev/null +++ b/src/testing/internal/scenarios/index.ts @@ -0,0 +1,110 @@ +import { ApolloLink, Observable, gql } from "../../../core/index.js"; +import type { TypedDocumentNode } from "../../../core/index.js"; +import type { MockedResponse } from "../../core/index.js"; + +export interface SimpleCaseData { + greeting: string; +} + +export function setupSimpleCase() { + const query: TypedDocumentNode> = gql` + query GreetingQuery { + greeting + } + `; + + const mocks: MockedResponse[] = [ + { + request: { query }, + result: { data: { greeting: "Hello" } }, + delay: 10, + }, + ]; + + return { query, mocks }; +} + +export interface VariablesCaseData { + character: { + __typename: "Character"; + id: string; + name: string; + }; +} + +export interface VariablesCaseVariables { + id: string; +} + +export function setupVariablesCase() { + const query: TypedDocumentNode = + gql` + query CharacterQuery($id: ID!) { + character(id: $id) { + id + name + } + } + `; + const CHARACTERS = ["Spider-Man", "Black Widow", "Iron Man", "Hulk"]; + + const mocks: MockedResponse[] = [...CHARACTERS].map( + (name, index) => ({ + request: { query, variables: { id: String(index + 1) } }, + result: { + data: { + character: { __typename: "Character", id: String(index + 1), name }, + }, + }, + delay: 20, + }) + ); + + return { mocks, query }; +} + +interface Letter { + letter: string; + position: number; +} + +export interface PaginatedCaseData { + letters: Letter[]; +} + +export interface PaginatedCaseVariables { + limit?: number; + offset?: number; +} + +export function setupPaginatedCase() { + const query: TypedDocumentNode = + gql` + query LettersQuery($limit: Int, $offset: Int) { + letters(limit: $limit, offset: $offset) { + letter + position + } + } + `; + + const data = "ABCDEFGHIJKLMNOPQRSTUV".split("").map((letter, index) => ({ + __typename: "Letter", + letter, + position: index + 1, + })); + + const link = new ApolloLink((operation) => { + const { offset = 0, limit = 2 } = operation.variables; + const letters = data.slice(offset, offset + limit); + + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { letters } }); + observer.complete(); + }, 10); + }); + }); + + return { query, link }; +} diff --git a/src/testing/matchers/ProfiledComponent.ts b/src/testing/matchers/ProfiledComponent.ts index c733d50e241..435c24a29a6 100644 --- a/src/testing/matchers/ProfiledComponent.ts +++ b/src/testing/matchers/ProfiledComponent.ts @@ -2,22 +2,22 @@ import type { MatcherFunction } from "expect"; import { WaitForRenderTimeoutError } from "../internal/index.js"; import type { NextRenderOptions, + Profiler, ProfiledComponent, ProfiledHook, } from "../internal/index.js"; + export const toRerender: MatcherFunction<[options?: NextRenderOptions]> = - async function ( - _profiled: ProfiledComponent | ProfiledHook, - options?: NextRenderOptions - ) { - const profiled = - "ProfiledComponent" in _profiled - ? _profiled.ProfiledComponent - : _profiled; - const hint = this.utils.matcherHint("toRerender"); + async function (actual, options) { + const _profiler = actual as + | Profiler + | ProfiledComponent + | ProfiledHook; + const profiler = "Profiler" in _profiler ? _profiler.Profiler : _profiler; + const hint = this.utils.matcherHint("toRerender", "ProfiledComponent", ""); let pass = true; try { - await profiled.peekRender({ timeout: 100, ...options }); + await profiler.peekRender({ timeout: 100, ...options }); } catch (e) { if (e instanceof WaitForRenderTimeoutError) { pass = false; @@ -25,12 +25,13 @@ export const toRerender: MatcherFunction<[options?: NextRenderOptions]> = throw e; } } + return { pass, message() { return ( hint + - ` Expected component to${pass ? " not" : ""} rerender, ` + + `\n\nExpected component to${pass ? " not" : ""} rerender, ` + `but it did${pass ? "" : " not"}.` ); }, @@ -42,30 +43,29 @@ const failed = {}; export const toRenderExactlyTimes: MatcherFunction< [times: number, options?: NextRenderOptions] -> = async function ( - _profiled: ProfiledComponent | ProfiledHook, - times: number, - optionsPerRender?: NextRenderOptions -) { - const profiled = - "ProfiledComponent" in _profiled ? _profiled.ProfiledComponent : _profiled; +> = async function (actual, times, optionsPerRender) { + const _profiler = actual as + | Profiler + | ProfiledComponent + | ProfiledHook; + const profiler = "Profiler" in _profiler ? _profiler.Profiler : _profiler; const options = { timeout: 100, ...optionsPerRender }; const hint = this.utils.matcherHint("toRenderExactlyTimes"); let pass = true; try { - if (profiled.currentRenderCount() > times) { + if (profiler.totalRenderCount() > times) { throw failed; } try { - while (profiled.currentRenderCount() < times) { - await profiled.waitForNextRender(options); + while (profiler.totalRenderCount() < times) { + await profiler.waitForNextRender(options); } } catch (e) { // timeouts here should just fail the test, rethrow other errors throw e instanceof WaitForRenderTimeoutError ? failed : e; } try { - await profiled.waitForNextRender(options); + await profiler.waitForNextRender(options); } catch (e) { // we are expecting a timeout here, so swallow that error, rethrow others if (!(e instanceof WaitForRenderTimeoutError)) { @@ -85,7 +85,7 @@ export const toRenderExactlyTimes: MatcherFunction< return ( hint + ` Expected component to${pass ? " not" : ""} render exactly ${times}.` + - ` It rendered ${profiled.currentRenderCount()} times.` + ` It rendered ${profiler.totalRenderCount()} times.` ); }, }; diff --git a/src/testing/matchers/index.d.ts b/src/testing/matchers/index.d.ts index 715f7d3dbdf..65fbcc37ba9 100644 --- a/src/testing/matchers/index.d.ts +++ b/src/testing/matchers/index.d.ts @@ -3,13 +3,20 @@ import type { DocumentNode, OperationVariables, } from "../../core/index.js"; +import type { QueryRef } from "../../react/index.js"; import { NextRenderOptions, + Profiler, ProfiledComponent, ProfiledHook, } from "../internal/index.js"; interface ApolloCustomMatchers { + /** + * Used to determine if a queryRef has been disposed. + */ + toBeDisposed: T extends QueryRef ? () => R + : { error: "matcher needs to be called on a QueryRef" }; /** * Used to determine if two GraphQL query documents are equal to each other by * comparing their printed values. The document must be parsed by `gql`. @@ -19,25 +26,30 @@ interface ApolloCustomMatchers { /** * Used to determine if the Suspense cache has a cache entry. */ - toHaveSuspenseCacheEntryUsing: T extends ApolloClient - ? ( - query: DocumentNode, - options?: { - variables?: OperationVariables; - queryKey?: string | number | any[]; - } - ) => R - : { error: "matcher needs to be called on an ApolloClient instance" }; + toHaveSuspenseCacheEntryUsing: T extends ApolloClient ? + ( + query: DocumentNode, + options?: { + variables?: OperationVariables; + queryKey?: string | number | any[]; + } + ) => R + : { error: "matcher needs to be called on an ApolloClient instance" }; + + toRerender: T extends ( + Profiler | ProfiledComponent | ProfiledHook + ) ? + (options?: NextRenderOptions) => Promise + : { error: "matcher needs to be called on a ProfiledComponent instance" }; - toRerender: T extends ProfiledComponent | ProfiledHook - ? (options?: NextRenderOptions) => Promise - : { error: "matcher needs to be called on a ProfiledComponent instance" }; + toRenderExactlyTimes: T extends ( + Profiler | ProfiledComponent | ProfiledHook + ) ? + (count: number, options?: NextRenderOptions) => Promise + : { error: "matcher needs to be called on a ProfiledComponent instance" }; - toRenderExactlyTimes: T extends - | ProfiledComponent - | ProfiledHook - ? (count: number, options?: NextRenderOptions) => Promise - : { error: "matcher needs to be called on a ProfiledComponent instance" }; + toBeGarbageCollected: T extends WeakRef ? () => Promise + : { error: "matcher needs to be called on a WeakRef instance" }; } declare global { diff --git a/src/testing/matchers/index.ts b/src/testing/matchers/index.ts index d2ebd8ce7c2..c4f88544f14 100644 --- a/src/testing/matchers/index.ts +++ b/src/testing/matchers/index.ts @@ -2,10 +2,14 @@ import { expect } from "@jest/globals"; import { toMatchDocument } from "./toMatchDocument.js"; import { toHaveSuspenseCacheEntryUsing } from "./toHaveSuspenseCacheEntryUsing.js"; import { toRerender, toRenderExactlyTimes } from "./ProfiledComponent.js"; +import { toBeGarbageCollected } from "./toBeGarbageCollected.js"; +import { toBeDisposed } from "./toBeDisposed.js"; expect.extend({ + toBeDisposed, toHaveSuspenseCacheEntryUsing, toMatchDocument, toRerender, toRenderExactlyTimes, + toBeGarbageCollected, }); diff --git a/src/testing/matchers/toBeDisposed.ts b/src/testing/matchers/toBeDisposed.ts new file mode 100644 index 00000000000..7b8d9461a8b --- /dev/null +++ b/src/testing/matchers/toBeDisposed.ts @@ -0,0 +1,26 @@ +import type { MatcherFunction } from "expect"; +import type { QueryRef } from "../../react/internal/index.js"; +import { + assertWrappedQueryRef, + unwrapQueryRef, +} from "../../react/internal/index.js"; + +export const toBeDisposed: MatcherFunction<[]> = function (_queryRef) { + const hint = this.utils.matcherHint("toBeDisposed", "queryRef", "", { + isNot: this.isNot, + }); + + const queryRef = _queryRef as QueryRef; + assertWrappedQueryRef(queryRef); + + const pass = unwrapQueryRef(queryRef).disposed; + + return { + pass, + message: () => { + return `${hint}\n\nExpected queryRef ${ + this.isNot ? "not " : "" + }to be disposed, but it was${this.isNot ? "" : " not"}.`; + }, + }; +}; diff --git a/src/testing/matchers/toBeGarbageCollected.ts b/src/testing/matchers/toBeGarbageCollected.ts new file mode 100644 index 00000000000..fda58543b38 --- /dev/null +++ b/src/testing/matchers/toBeGarbageCollected.ts @@ -0,0 +1,59 @@ +import type { MatcherFunction } from "expect"; + +// this is necessary because this file is picked up by `tsc` (it's not a test), +// but our main `tsconfig.json` doesn't include `"ES2021.WeakRef"` on purpose +declare class WeakRef { + constructor(target: T); + deref(): T | undefined; +} + +export const toBeGarbageCollected: MatcherFunction<[weakRef: WeakRef]> = + async function (actual) { + const hint = this.utils.matcherHint("toBeGarbageCollected"); + + if (!(actual instanceof WeakRef)) { + throw new Error( + hint + + "\n\n" + + `Expected value to be a WeakRef, but it was a ${typeof actual}.` + ); + } + + let pass = false; + let interval: NodeJS.Timeout | undefined; + let timeout: NodeJS.Timeout | undefined; + await Promise.race([ + new Promise((resolve) => { + timeout = setTimeout(resolve, 1000); + }), + new Promise((resolve) => { + interval = setInterval(() => { + global.gc!(); + pass = actual.deref() === undefined; + if (pass) { + resolve(); + } + }, 1); + }), + ]); + + clearInterval(interval); + clearTimeout(timeout); + + return { + pass, + message: () => { + if (pass) { + return ( + hint + + "\n\n" + + "Expected value to not be cache-collected, but it was." + ); + } + + return ( + hint + "\n\n Expected value to be cache-collected, but it was not." + ); + }, + }; + }; diff --git a/src/testing/matchers/toHaveSuspenseCacheEntryUsing.ts b/src/testing/matchers/toHaveSuspenseCacheEntryUsing.ts index 7244952fed6..9cfdd2c1d78 100644 --- a/src/testing/matchers/toHaveSuspenseCacheEntryUsing.ts +++ b/src/testing/matchers/toHaveSuspenseCacheEntryUsing.ts @@ -3,8 +3,8 @@ import type { DocumentNode } from "graphql"; import type { OperationVariables } from "../../core/index.js"; import { ApolloClient } from "../../core/index.js"; import { canonicalStringify } from "../../cache/index.js"; -import { getSuspenseCache } from "../../react/cache/index.js"; -import type { CacheKey } from "../../react/cache/types.js"; +import { getSuspenseCache } from "../../react/internal/index.js"; +import type { CacheKey } from "../../react/internal/index.js"; export const toHaveSuspenseCacheEntryUsing: MatcherFunction< [ diff --git a/src/testing/react/MockedProvider.tsx b/src/testing/react/MockedProvider.tsx index b7afb9d416c..fa06db3c324 100644 --- a/src/testing/react/MockedProvider.tsx +++ b/src/testing/react/MockedProvider.tsx @@ -11,7 +11,7 @@ import type { Resolvers } from "../../core/index.js"; import type { ApolloCache } from "../../cache/index.js"; export interface MockedProviderProps { - mocks?: ReadonlyArray; + mocks?: ReadonlyArray>; addTypename?: boolean; defaultOptions?: DefaultOptions; cache?: ApolloCache; @@ -69,11 +69,11 @@ export class MockedProvider extends React.Component< const { children, childProps } = this.props; const { client } = this.state; - return React.isValidElement(children) ? ( - - {React.cloneElement(React.Children.only(children), { ...childProps })} - - ) : null; + return React.isValidElement(children) ? + + {React.cloneElement(React.Children.only(children), { ...childProps })} + + : null; } public componentWillUnmount() { diff --git a/src/testing/react/__tests__/MockedProvider.test.tsx b/src/testing/react/__tests__/MockedProvider.test.tsx index eb18f3b3a9f..4ebd0878a43 100644 --- a/src/testing/react/__tests__/MockedProvider.test.tsx +++ b/src/testing/react/__tests__/MockedProvider.test.tsx @@ -1,14 +1,15 @@ import React from "react"; import { DocumentNode } from "graphql"; -import { render, screen, waitFor } from "@testing-library/react"; +import { act, render, screen, waitFor } from "@testing-library/react"; import gql from "graphql-tag"; import { itAsync, MockedResponse, MockLink } from "../../core"; import { MockedProvider } from "../MockedProvider"; import { useQuery } from "../../../react/hooks"; import { InMemoryCache } from "../../../cache"; -import { ApolloLink } from "../../../link/core"; -import { spyOnConsole } from "../../internal"; +import { QueryResult } from "../../../react/types/types"; +import { ApolloLink, FetchResult } from "../../../link/core"; +import { Observable } from "zen-observable-ts"; const variables = { username: "mock_username", @@ -56,13 +57,17 @@ interface Data { }; } +interface Result { + current: QueryResult | null; +} + interface Variables { username: string; } let errorThrown = false; const errorLink = new ApolloLink((operation, forward) => { - let observer = null; + let observer: Observable | null = null; try { observer = forward(operation); } catch (error) { @@ -98,6 +103,100 @@ describe("General use", () => { }).then(resolve, reject); }); + itAsync( + "should pass the variables to the result function", + async (resolve, reject) => { + function Component({ ...variables }: Variables) { + useQuery(query, { variables }); + return null; + } + + const mock2: MockedResponse = { + request: { + query, + variables, + }, + result: jest.fn().mockResolvedValue({ data: { user } }), + }; + + render( + + + + ); + + waitFor(() => { + expect(mock2.result as jest.Mock).toHaveBeenCalledWith(variables); + }).then(resolve, reject); + } + ); + + itAsync( + "should pass the variables to the variableMatcher", + async (resolve, reject) => { + function Component({ ...variables }: Variables) { + useQuery(query, { variables }); + return null; + } + + const mock2: MockedResponse = { + request: { + query, + }, + variableMatcher: jest.fn().mockReturnValue(true), + result: { data: { user } }, + }; + + render( + + + + ); + + waitFor(() => { + expect(mock2.variableMatcher as jest.Mock).toHaveBeenCalledWith( + variables + ); + }).then(resolve, reject); + } + ); + + itAsync( + "should use a mock if the variableMatcher returns true", + async (resolve, reject) => { + let finished = false; + + function Component({ username }: Variables) { + const { loading, data } = useQuery(query, { + variables, + }); + if (!loading) { + expect(data!.user).toMatchSnapshot(); + finished = true; + } + return null; + } + + const mock2: MockedResponse = { + request: { + query, + }, + variableMatcher: (v) => v.username === variables.username, + result: { data: { user } }, + }; + + render( + + + + ); + + waitFor(() => { + expect(finished).toBe(true); + }).then(resolve, reject); + } + ); + itAsync("should allow querying with the typename", (resolve, reject) => { let finished = false; function Component({ username }: Variables) { @@ -191,6 +290,41 @@ describe("General use", () => { } ); + itAsync( + "should error if the variableMatcher returns false", + async (resolve, reject) => { + let finished = false; + function Component({ ...variables }: Variables) { + const { loading, error } = useQuery(query, { + variables, + }); + if (!loading) { + expect(error).toMatchSnapshot(); + finished = true; + } + return null; + } + + const mock2: MockedResponse = { + request: { + query, + }, + variableMatcher: () => false, + result: { data: { user } }, + }; + + render( + + + + ); + + waitFor(() => { + expect(finished).toBe(true); + }).then(resolve, reject); + } + ); + itAsync( "should error if the variables do not deep equal", (resolve, reject) => { @@ -482,6 +616,243 @@ describe("General use", () => { expect(errorThrown).toBeFalsy(); }); + it("Uses a mock a configured number of times when `maxUsageCount` is configured", async () => { + const result: Result = { current: null }; + function Component({ username }: Variables) { + result.current = useQuery(query, { + variables: { username }, + }); + return null; + } + + const waitForLoaded = async () => { + await waitFor(() => { + expect(result.current?.loading).toBe(false); + expect(result.current?.error).toBeUndefined(); + }); + }; + + const waitForError = async () => { + await waitFor(() => { + expect(result.current?.error?.message).toMatch( + /No more mocked responses/ + ); + }); + }; + + const refetch = () => { + return act(async () => { + try { + await result.current?.refetch(); + } catch {} + }); + }; + + const mocks: ReadonlyArray = [ + { + request: { + query, + variables: { + username: "mock_username", + }, + }, + maxUsageCount: 2, + result: { data: { user } }, + }, + ]; + + const mockLink = new MockLink(mocks, true, { showWarnings: false }); + const link = ApolloLink.from([errorLink, mockLink]); + const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + render(, { wrapper: Wrapper }); + await waitForLoaded(); + await refetch(); + await waitForLoaded(); + await refetch(); + await waitForError(); + }); + + it("Uses a mock infinite number of times when `maxUsageCount` is configured with Number.POSITIVE_INFINITY", async () => { + const result: Result = { current: null }; + function Component({ username }: Variables) { + result.current = useQuery(query, { + variables: { username }, + }); + return null; + } + + const waitForLoaded = async () => { + await waitFor(() => { + expect(result.current?.loading).toBe(false); + expect(result.current?.error).toBeUndefined(); + }); + }; + + const refetch = () => { + return act(async () => { + try { + await result.current?.refetch(); + } catch {} + }); + }; + + const mocks: ReadonlyArray = [ + { + request: { + query, + variables: { + username: "mock_username", + }, + }, + maxUsageCount: Number.POSITIVE_INFINITY, + result: { data: { user } }, + }, + ]; + + const mockLink = new MockLink(mocks, true, { showWarnings: false }); + const link = ApolloLink.from([errorLink, mockLink]); + const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + render(, { wrapper: Wrapper }); + for (let i = 0; i < 100; i++) { + await waitForLoaded(); + await refetch(); + } + await waitForLoaded(); + }); + + it("uses a mock once when `maxUsageCount` is not configured", async () => { + const result: Result = { current: null }; + function Component({ username }: Variables) { + result.current = useQuery(query, { + variables: { username }, + }); + return null; + } + + const waitForLoaded = async () => { + await waitFor(() => { + expect(result.current?.loading).toBe(false); + expect(result.current?.error).toBeUndefined(); + }); + }; + + const waitForError = async () => { + await waitFor(() => { + expect(result.current?.error?.message).toMatch( + /No more mocked responses/ + ); + }); + }; + + const refetch = () => { + return act(async () => { + try { + await result.current?.refetch(); + } catch {} + }); + }; + + const mocks: ReadonlyArray = [ + { + request: { + query, + variables: { + username: "mock_username", + }, + }, + result: { data: { user } }, + }, + ]; + + const mockLink = new MockLink(mocks, true, { showWarnings: false }); + const link = ApolloLink.from([errorLink, mockLink]); + const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + render(, { wrapper: Wrapper }); + await waitForLoaded(); + await refetch(); + await waitForError(); + }); + + it("can still use other mocks after a mock has been fully consumed", async () => { + const result: Result = { current: null }; + function Component({ username }: Variables) { + result.current = useQuery(query, { + variables: { username }, + }); + return null; + } + + const waitForLoaded = async () => { + await waitFor(() => { + expect(result.current?.loading).toBe(false); + expect(result.current?.error).toBeUndefined(); + }); + }; + + const refetch = () => { + return act(async () => { + try { + await result.current?.refetch(); + } catch {} + }); + }; + + const mocks: ReadonlyArray = [ + { + request: { + query, + variables: { + username: "mock_username", + }, + }, + maxUsageCount: 2, + result: { data: { user } }, + }, + { + request: { + query, + variables: { + username: "mock_username", + }, + }, + result: { + data: { + user: { + __typename: "User", + id: "new_id", + }, + }, + }, + }, + ]; + + const mockLink = new MockLink(mocks, true, { showWarnings: false }); + const link = ApolloLink.from([errorLink, mockLink]); + const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + render(, { wrapper: Wrapper }); + await waitForLoaded(); + await refetch(); + await waitForLoaded(); + await refetch(); + await waitForLoaded(); + expect(result.current?.data?.user).toEqual({ + __typename: "User", + id: "new_id", + }); + }); + it('should return "Mocked response should contain" errors in response', async () => { let finished = false; function Component({ ...variables }: Variables) { @@ -522,7 +893,7 @@ describe("General use", () => { }); it("shows a warning in the console when there is no matched mock", async () => { - using _consoleSpy = spyOnConsole("warn"); + const consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); let finished = false; function Component({ ...variables }: Variables) { const { loading } = useQuery(query, { variables }); @@ -562,10 +933,12 @@ describe("General use", () => { expect(console.warn).toHaveBeenCalledWith( expect.stringContaining("No more mocked responses for the query") ); + + consoleSpy.mockRestore(); }); it("silences console warning for unmatched mocks when `showWarnings` is `false`", async () => { - using _consoleSpy = spyOnConsole("warn"); + const consoleSpy = jest.spyOn(console, "warn"); let finished = false; function Component({ ...variables }: Variables) { const { loading } = useQuery(query, { variables }); @@ -602,10 +975,12 @@ describe("General use", () => { }); expect(console.warn).not.toHaveBeenCalled(); + + consoleSpy.mockRestore(); }); it("silences console warning for unmatched mocks when passing `showWarnings` to `MockLink` directly", async () => { - using _consoleSpy = spyOnConsole("warn"); + const consoleSpy = jest.spyOn(console, "warn"); let finished = false; function Component({ ...variables }: Variables) { const { loading } = useQuery(query, { variables }); @@ -646,6 +1021,8 @@ describe("General use", () => { }); expect(console.warn).not.toHaveBeenCalled(); + + consoleSpy.mockRestore(); }); itAsync( @@ -710,6 +1087,8 @@ describe("General use", () => { ); it("should support loading state testing with delay", async () => { + jest.useFakeTimers(); + function Component({ username }: Variables) { const { loading, data } = useQuery(query, { variables }); @@ -720,7 +1099,7 @@ describe("General use", () => { const mocks: ReadonlyArray = [ { - delay: 30, // prevent React from batching the loading state away + delay: 30000, // prevent React from batching the loading state away request: { query, variables, @@ -738,16 +1117,26 @@ describe("General use", () => { expect( await screen.findByText("Loading the user ID...") ).toBeInTheDocument(); + + jest.advanceTimersByTime(30_000); + expect( await screen.findByText("The user ID is 'user_id'") ).toBeInTheDocument(); + + jest.useRealTimers(); }); - it("should support loading state testing with infinite delay", async () => { + it("should support an infinite loading state with result and delay: Infinity", async () => { + jest.useFakeTimers(); + function Component({ username }: Variables) { - const { loading, data } = useQuery(query, { variables }); + const { loading, data } = useQuery(query, { + variables, + }); - if (loading || data === undefined) return

Loading the user ID...

; + if (loading) return

Loading the user ID...

; + if (data === undefined) return

Undefined data

; return

The user ID is '{data.user.id}'

; } @@ -759,6 +1148,7 @@ describe("General use", () => { query, variables, }, + result: { data: { user } }, }, ]; @@ -771,6 +1161,62 @@ describe("General use", () => { expect( await screen.findByText("Loading the user ID...") ).toBeInTheDocument(); + + jest.advanceTimersByTime(Number.MAX_SAFE_INTEGER); + + expect( + await screen.findByText("Loading the user ID...") + ).toBeInTheDocument(); + + expect(screen.queryByText(/The user ID is/i)).toBeNull(); + + jest.useRealTimers(); + }); + + it("should support an infinite loading state with error and delay: Infinity", async () => { + jest.useFakeTimers(); + + function Component({ username }: Variables) { + const { loading, data } = useQuery(query, { + variables, + }); + + if (loading) return

Loading the user ID...

; + if (data === undefined) return

Undefined data

; + + return

The user ID is '{data.user.id}'

; + } + + const mocks: ReadonlyArray = [ + { + delay: Infinity, // keep loading forever. + request: { + query, + variables, + }, + error: new Error("something went wrong"), + }, + ]; + + render( + + + + ); + + expect( + await screen.findByText("Loading the user ID...") + ).toBeInTheDocument(); + + jest.advanceTimersByTime(Number.MAX_SAFE_INTEGER); + + expect( + await screen.findByText("Loading the user ID...") + ).toBeInTheDocument(); + + expect(screen.queryByText(/The user ID is/i)).toBeNull(); + + jest.useRealTimers(); }); }); diff --git a/src/testing/react/__tests__/__snapshots__/MockedProvider.test.tsx.snap b/src/testing/react/__tests__/__snapshots__/MockedProvider.test.tsx.snap index 5fecc4e98d7..6706568ffd2 100644 --- a/src/testing/react/__tests__/__snapshots__/MockedProvider.test.tsx.snap +++ b/src/testing/react/__tests__/__snapshots__/MockedProvider.test.tsx.snap @@ -18,6 +18,20 @@ Expected variables: {"username":"mock_username"} ] `; +exports[`General use should error if the variableMatcher returns false 1`] = ` +[ApolloError: No more mocked responses for the query: query GetUser($username: String!) { + user(username: $username) { + id + __typename + } +} +Expected variables: {"username":"mock_username"} + +Failed to match 1 mock for this query. The mocked response had the following variables: + {} +] +`; + exports[`General use should error if the variables do not deep equal 1`] = ` [ApolloError: No more mocked responses for the query: query GetUser($username: String!) { user(username: $username) { @@ -64,7 +78,7 @@ Object { exports[`General use should pipe exceptions thrown in custom onError functions through the link chain 1`] = `[ApolloError: oh no!]`; -exports[`General use should return "Mocked response should contain" errors in response 1`] = `[ApolloError: Mocked response should contain either result or error: {"query":"query GetUser($username: String!) {\\n user(username: $username) {\\n id\\n __typename\\n }\\n}"}]`; +exports[`General use should return "Mocked response should contain" errors in response 1`] = `[ApolloError: Mocked response should contain either \`result\`, \`error\` or a \`delay\` of \`Infinity\`: {"query":"query GetUser($username: String!) {\\n user(username: $username) {\\n id\\n __typename\\n }\\n}"}]`; exports[`General use should return "No more mocked responses" errors in response 1`] = ` [ApolloError: No more mocked responses for the query: query GetUser($username: String!) { @@ -87,3 +101,10 @@ exports[`General use should support custom error handling using setOnError 1`] = Expected variables: {"username":"mock_username"} ] `; + +exports[`General use should use a mock if the variableMatcher returns true 1`] = ` +Object { + "__typename": "User", + "id": "user_id", +} +`; diff --git a/src/testing/react/__tests__/mockSubscriptionLink.test.tsx b/src/testing/react/__tests__/mockSubscriptionLink.test.tsx index 0515e45cb0a..8b26aea0dd3 100644 --- a/src/testing/react/__tests__/mockSubscriptionLink.test.tsx +++ b/src/testing/react/__tests__/mockSubscriptionLink.test.tsx @@ -9,6 +9,7 @@ import { ApolloProvider } from "../../../react/context"; import { useSubscription } from "../../../react/hooks"; const IS_REACT_18 = React.version.startsWith("18"); +const IS_REACT_19 = React.version.startsWith("19"); describe("mockSubscriptionLink", () => { it("should work with multiple subscribers to the same mock websocket", async () => { @@ -64,7 +65,7 @@ describe("mockSubscriptionLink", () => { ); - const numRenders = IS_REACT_18 ? 2 : results.length + 1; + const numRenders = IS_REACT_18 || IS_REACT_19 ? 2 : results.length + 1; // automatic batching in React 18 means we only see 2 renders vs. 5 in v17 await waitFor( diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 00000000000..efeb2f2da38 --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,14 @@ +// `tsconfig.json` for the editor only +// this config includes additional files (e.g. tests) that +// we don't want to see hooked up to the main build +// it can also add a few more types for that purpose +{ + "compilerOptions": { + "noEmit": true, + "lib": ["es2015", "esnext.asynciterable", "ES2021.WeakRef"], + "types": ["jest", "node", "./testing/matchers/index.d.ts"] + }, + "extends": "../tsconfig.json", + "include": ["./**/*.ts", "./**/*.tsx"], + "exclude": [] +} diff --git a/src/utilities/caching/__tests__/getMemoryInternals.ts b/src/utilities/caching/__tests__/getMemoryInternals.ts new file mode 100644 index 00000000000..c83f34da27f --- /dev/null +++ b/src/utilities/caching/__tests__/getMemoryInternals.ts @@ -0,0 +1,204 @@ +import { createFragmentRegistry } from "../../../cache"; +import { + ApolloClient, + ApolloLink, + DocumentTransform, + InMemoryCache, + gql, +} from "../../../core"; +import { createPersistedQueryLink } from "../../../link/persisted-queries"; +import { removeTypenameFromVariables } from "../../../link/remove-typename"; +import crypto from "crypto"; +// importing react so the `parser` cache initializes +import "../../../react"; +import { cacheSizes, defaultCacheSizes } from "../sizes"; + +function sha256(data: string) { + const hash = crypto.createHash("sha256"); + hash.update(data); + return hash.digest("hex"); +} + +const defaultCacheSizesAsObject = { + parser: defaultCacheSizes["parser"], + canonicalStringify: defaultCacheSizes["canonicalStringify"], + print: defaultCacheSizes["print"], + "documentTransform.cache": defaultCacheSizes["documentTransform.cache"], + "queryManager.getDocumentInfo": + defaultCacheSizes["queryManager.getDocumentInfo"], + "PersistedQueryLink.persistedQueryHashes": + defaultCacheSizes["PersistedQueryLink.persistedQueryHashes"], + "fragmentRegistry.transform": defaultCacheSizes["fragmentRegistry.transform"], + "fragmentRegistry.lookup": defaultCacheSizes["fragmentRegistry.lookup"], + "fragmentRegistry.findFragmentSpreads": + defaultCacheSizes["fragmentRegistry.findFragmentSpreads"], + "cache.fragmentQueryDocuments": + defaultCacheSizes["cache.fragmentQueryDocuments"], + "removeTypenameFromVariables.getVariableDefinitions": + defaultCacheSizes["removeTypenameFromVariables.getVariableDefinitions"], + "inMemoryCache.maybeBroadcastWatch": + defaultCacheSizes["inMemoryCache.maybeBroadcastWatch"], + "inMemoryCache.executeSelectionSet": + defaultCacheSizes["inMemoryCache.executeSelectionSet"], + "inMemoryCache.executeSubSelectedArray": + defaultCacheSizes["inMemoryCache.executeSubSelectedArray"], +}; + +it("returns information about cache usage (empty caches)", () => { + const client = new ApolloClient({ + documentTransform: new DocumentTransform((x) => x, { + cache: true, + }).concat( + new DocumentTransform((x) => x, { + cache: true, + }) + ), + cache: new InMemoryCache({ + fragments: createFragmentRegistry(), + }), + link: createPersistedQueryLink({ + sha256, + }) + .concat(removeTypenameFromVariables()) + .concat(ApolloLink.empty()), + }); + expect(client.getMemoryInternals?.()).toEqual({ + limits: defaultCacheSizesAsObject, + sizes: { + parser: 0, + canonicalStringify: 0, + print: 0, + addTypenameDocumentTransform: [ + { + cache: 0, + }, + ], + queryManager: { + getDocumentInfo: 0, + documentTransforms: [ + { + cache: 0, + }, + { + cache: 0, + }, + ], + }, + fragmentRegistry: { + findFragmentSpreads: 0, + lookup: 0, + transform: 0, + }, + cache: { + fragmentQueryDocuments: 0, + }, + inMemoryCache: { + executeSelectionSet: 0, + executeSubSelectedArray: 0, + maybeBroadcastWatch: 0, + }, + links: [ + { + PersistedQueryLink: { + persistedQueryHashes: 0, + }, + }, + { + removeTypenameFromVariables: { + getVariableDefinitions: 0, + }, + }, + ], + }, + }); +}); + +it("returns information about cache usage (some query triggered)", () => { + const client = new ApolloClient({ + documentTransform: new DocumentTransform((x) => x, { + cache: true, + }).concat( + new DocumentTransform((x) => x, { + cache: true, + }) + ), + cache: new InMemoryCache({ + fragments: createFragmentRegistry(), + }), + link: createPersistedQueryLink({ + sha256, + }) + .concat(removeTypenameFromVariables()) + .concat(ApolloLink.empty()), + }); + + client.query({ + query: gql` + query { + hello + } + `, + }); + expect(client.getMemoryInternals?.()).toStrictEqual({ + limits: defaultCacheSizesAsObject, + sizes: { + parser: 0, + canonicalStringify: 0, + print: 1, + addTypenameDocumentTransform: [ + { + cache: 1, + }, + ], + queryManager: { + getDocumentInfo: 1, + documentTransforms: [ + { + cache: 1, + }, + { + cache: 1, + }, + ], + }, + fragmentRegistry: { + findFragmentSpreads: 1, + lookup: 0, + transform: 1, + }, + cache: { + fragmentQueryDocuments: 0, + }, + inMemoryCache: { + executeSelectionSet: 1, + executeSubSelectedArray: 0, + maybeBroadcastWatch: 0, + }, + links: [ + { + PersistedQueryLink: { + persistedQueryHashes: 1, + }, + }, + { + removeTypenameFromVariables: { + getVariableDefinitions: 0, + }, + }, + ], + }, + }); +}); + +it("reports user-declared cacheSizes", () => { + const client = new ApolloClient({ + cache: new InMemoryCache({}), + }); + + cacheSizes["inMemoryCache.executeSubSelectedArray"] = 90; + + expect(client.getMemoryInternals?.().limits).toStrictEqual({ + ...defaultCacheSizesAsObject, + "inMemoryCache.executeSubSelectedArray": 90, + }); +}); diff --git a/src/utilities/caching/__tests__/sizes.test.ts b/src/utilities/caching/__tests__/sizes.test.ts new file mode 100644 index 00000000000..8339c8950f3 --- /dev/null +++ b/src/utilities/caching/__tests__/sizes.test.ts @@ -0,0 +1,11 @@ +import { expectTypeOf } from "expect-type"; +import type { CacheSizes, defaultCacheSizes } from "../sizes"; + +test.skip("type tests", () => { + expectTypeOf().toMatchTypeOf< + keyof typeof defaultCacheSizes + >(); + expectTypeOf().toMatchTypeOf< + keyof CacheSizes + >(); +}); diff --git a/src/utilities/caching/caches.ts b/src/utilities/caching/caches.ts new file mode 100644 index 00000000000..a42cd24a06d --- /dev/null +++ b/src/utilities/caching/caches.ts @@ -0,0 +1,89 @@ +import { WeakCache, StrongCache } from "@wry/caches"; + +interface CleanableCache { + size: number; + max?: number; + clean: () => void; +} +const scheduledCleanup = new WeakSet(); +function schedule(cache: CleanableCache) { + if (cache.size <= (cache.max || -1)) { + return; + } + if (!scheduledCleanup.has(cache)) { + scheduledCleanup.add(cache); + setTimeout(() => { + cache.clean(); + scheduledCleanup.delete(cache); + }, 100); + } +} +/** + * @internal + * A version of WeakCache that will auto-schedule a cleanup of the cache when + * a new item is added and the cache reached maximum size. + * Throttled to once per 100ms. + * + * @privateRemarks + * Should be used throughout the rest of the codebase instead of WeakCache, + * with the notable exception of usage in `wrap` from `optimism` - that one + * already handles cleanup and should remain a `WeakCache`. + */ +export const AutoCleanedWeakCache = function ( + max?: number | undefined, + dispose?: ((value: any, key: any) => void) | undefined +) { + /* + Some builds of `WeakCache` are function prototypes, some are classes. + This library still builds with an ES5 target, so we can't extend the + real classes. + Instead, we have to use this workaround until we switch to a newer build + target. + */ + const cache = new WeakCache(max, dispose); + cache.set = function (key: any, value: any) { + const ret = WeakCache.prototype.set.call(this, key, value); + schedule(this as any as CleanableCache); + return ret; + }; + return cache; +} as any as typeof WeakCache; +/** + * @internal + */ +export type AutoCleanedWeakCache = WeakCache; + +/** + * @internal + * A version of StrongCache that will auto-schedule a cleanup of the cache when + * a new item is added and the cache reached maximum size. + * Throttled to once per 100ms. + * + * @privateRemarks + * Should be used throughout the rest of the codebase instead of StrongCache, + * with the notable exception of usage in `wrap` from `optimism` - that one + * already handles cleanup and should remain a `StrongCache`. + */ +export const AutoCleanedStrongCache = function ( + max?: number | undefined, + dispose?: ((value: any, key: any) => void) | undefined +) { + /* + Some builds of `StrongCache` are function prototypes, some are classes. + This library still builds with an ES5 target, so we can't extend the + real classes. + Instead, we have to use this workaround until we switch to a newer build + target. + */ + const cache = new StrongCache(max, dispose); + cache.set = function (key: any, value: any) { + const ret = StrongCache.prototype.set.call(this, key, value); + schedule(this as any as CleanableCache); + return ret; + }; + return cache; +} as any as typeof StrongCache; +/** + * @internal + */ +export type AutoCleanedStrongCache = StrongCache; diff --git a/src/utilities/caching/getMemoryInternals.ts b/src/utilities/caching/getMemoryInternals.ts new file mode 100644 index 00000000000..ac28989c37b --- /dev/null +++ b/src/utilities/caching/getMemoryInternals.ts @@ -0,0 +1,228 @@ +import type { OptimisticWrapperFunction } from "optimism"; +import type { + InMemoryCache, + DocumentTransform, + ApolloLink, + ApolloCache, +} from "../../core/index.js"; +import type { ApolloClient } from "../../core/index.js"; +import type { CacheSizes } from "./sizes.js"; +import { cacheSizes, defaultCacheSizes } from "./sizes.js"; + +const globalCaches: { + print?: () => number; + parser?: () => number; + canonicalStringify?: () => number; +} = {}; + +export function registerGlobalCache( + name: keyof typeof globalCaches, + getSize: () => number +) { + globalCaches[name] = getSize; +} + +/** + * Transformative helper type to turn a function of the form + * ```ts + * (this: any) => R + * ``` + * into a function of the form + * ```ts + * () => R + * ``` + * preserving the return type, but removing the `this` parameter. + * + * @remarks + * + * Further down in the definitions of `_getApolloClientMemoryInternals`, + * `_getApolloCacheMemoryInternals` and `_getInMemoryCacheMemoryInternals`, + * having the `this` parameter annotation is extremely useful for type checking + * inside the function. + * + * If this is preserved in the exported types, though, it leads to a situation + * where `ApolloCache.getMemoryInternals` is a function that requires a `this` + * of the type `ApolloCache`, while the extending class `InMemoryCache` has a + * `getMemoryInternals` function that requires a `this` of the type + * `InMemoryCache`. + * This is not compatible with TypeScript's inheritence system (although it is + * perfectly correct), and so TypeScript will complain loudly. + * + * We still want to define our functions with the `this` annotation, though, + * and have the return type inferred. + * (This requirement for return type inference here makes it impossible to use + * a function overload that is more explicit on the inner overload than it is + * on the external overload.) + * + * So in the end, we use this helper to remove the `this` annotation from the + * exported function types, while keeping it in the internal implementation. + * + */ +type RemoveThis = T extends (this: any) => infer R ? () => R : never; + +/** + * For internal purposes only - please call `ApolloClient.getMemoryInternals` instead + * @internal + */ +export const getApolloClientMemoryInternals = + __DEV__ ? + (_getApolloClientMemoryInternals as RemoveThis< + typeof _getApolloClientMemoryInternals + >) + : undefined; + +/** + * For internal purposes only - please call `ApolloClient.getMemoryInternals` instead + * @internal + */ +export const getInMemoryCacheMemoryInternals = + __DEV__ ? + (_getInMemoryCacheMemoryInternals as RemoveThis< + typeof _getInMemoryCacheMemoryInternals + >) + : undefined; + +/** + * For internal purposes only - please call `ApolloClient.getMemoryInternals` instead + * @internal + */ +export const getApolloCacheMemoryInternals = + __DEV__ ? + (_getApolloCacheMemoryInternals as RemoveThis< + typeof _getApolloCacheMemoryInternals + >) + : undefined; + +function getCurrentCacheSizes() { + // `defaultCacheSizes` is a `const enum` that will be inlined during build, so we have to reconstruct it's shape here + const defaults: Record = { + parser: defaultCacheSizes["parser"], + canonicalStringify: defaultCacheSizes["canonicalStringify"], + print: defaultCacheSizes["print"], + "documentTransform.cache": defaultCacheSizes["documentTransform.cache"], + "queryManager.getDocumentInfo": + defaultCacheSizes["queryManager.getDocumentInfo"], + "PersistedQueryLink.persistedQueryHashes": + defaultCacheSizes["PersistedQueryLink.persistedQueryHashes"], + "fragmentRegistry.transform": + defaultCacheSizes["fragmentRegistry.transform"], + "fragmentRegistry.lookup": defaultCacheSizes["fragmentRegistry.lookup"], + "fragmentRegistry.findFragmentSpreads": + defaultCacheSizes["fragmentRegistry.findFragmentSpreads"], + "cache.fragmentQueryDocuments": + defaultCacheSizes["cache.fragmentQueryDocuments"], + "removeTypenameFromVariables.getVariableDefinitions": + defaultCacheSizes["removeTypenameFromVariables.getVariableDefinitions"], + "inMemoryCache.maybeBroadcastWatch": + defaultCacheSizes["inMemoryCache.maybeBroadcastWatch"], + "inMemoryCache.executeSelectionSet": + defaultCacheSizes["inMemoryCache.executeSelectionSet"], + "inMemoryCache.executeSubSelectedArray": + defaultCacheSizes["inMemoryCache.executeSubSelectedArray"], + }; + return Object.fromEntries( + Object.entries(defaults).map(([k, v]) => [ + k, + cacheSizes[k as keyof CacheSizes] || v, + ]) + ); +} + +function _getApolloClientMemoryInternals(this: ApolloClient) { + if (!__DEV__) throw new Error("only supported in development mode"); + + return { + limits: getCurrentCacheSizes(), + sizes: { + print: globalCaches.print?.(), + parser: globalCaches.parser?.(), + canonicalStringify: globalCaches.canonicalStringify?.(), + links: linkInfo(this.link), + queryManager: { + getDocumentInfo: this["queryManager"]["transformCache"].size, + documentTransforms: transformInfo( + this["queryManager"].documentTransform + ), + }, + ...(this.cache.getMemoryInternals?.() as Partial< + ReturnType + > & + Partial>), + }, + }; +} + +function _getApolloCacheMemoryInternals(this: ApolloCache) { + return { + cache: { + fragmentQueryDocuments: getWrapperInformation(this["getFragmentDoc"]), + }, + }; +} + +function _getInMemoryCacheMemoryInternals(this: InMemoryCache) { + const fragments = this.config.fragments as + | undefined + | { + findFragmentSpreads?: Function; + transform?: Function; + lookup?: Function; + }; + + return { + ..._getApolloCacheMemoryInternals.apply(this as any), + addTypenameDocumentTransform: transformInfo(this["addTypenameTransform"]), + inMemoryCache: { + executeSelectionSet: getWrapperInformation( + this["storeReader"]["executeSelectionSet"] + ), + executeSubSelectedArray: getWrapperInformation( + this["storeReader"]["executeSubSelectedArray"] + ), + maybeBroadcastWatch: getWrapperInformation(this["maybeBroadcastWatch"]), + }, + fragmentRegistry: { + findFragmentSpreads: getWrapperInformation( + fragments?.findFragmentSpreads + ), + lookup: getWrapperInformation(fragments?.lookup), + transform: getWrapperInformation(fragments?.transform), + }, + }; +} + +function isWrapper(f?: Function): f is OptimisticWrapperFunction { + return !!f && "dirtyKey" in f; +} + +function getWrapperInformation(f?: Function) { + return isWrapper(f) ? f.size : undefined; +} + +function isDefined(value: T | undefined | null): value is T { + return value != null; +} + +function transformInfo(transform?: DocumentTransform) { + return recurseTransformInfo(transform).map((cache) => ({ cache })); +} + +function recurseTransformInfo(transform?: DocumentTransform): number[] { + return transform ? + [ + getWrapperInformation(transform?.["performWork"]), + ...recurseTransformInfo(transform?.["left"]), + ...recurseTransformInfo(transform?.["right"]), + ].filter(isDefined) + : []; +} + +function linkInfo(link?: ApolloLink): unknown[] { + return link ? + [ + link?.getMemoryInternals?.(), + ...linkInfo(link?.left), + ...linkInfo(link?.right), + ].filter(isDefined) + : []; +} diff --git a/src/utilities/caching/index.ts b/src/utilities/caching/index.ts new file mode 100644 index 00000000000..159dc27fcfd --- /dev/null +++ b/src/utilities/caching/index.ts @@ -0,0 +1,3 @@ +export { AutoCleanedStrongCache, AutoCleanedWeakCache } from "./caches.js"; +export type { CacheSizes } from "./sizes.js"; +export { cacheSizes, defaultCacheSizes } from "./sizes.js"; diff --git a/src/utilities/caching/sizes.ts b/src/utilities/caching/sizes.ts new file mode 100644 index 00000000000..114ce2118bc --- /dev/null +++ b/src/utilities/caching/sizes.ts @@ -0,0 +1,319 @@ +import { global } from "../globals/index.js"; + +declare global { + interface Window { + [cacheSizeSymbol]?: Partial; + } +} + +/** + * The cache sizes used by various Apollo Client caches. + * + * @remarks + * All configurable caches hold memoized values. If an item is + * cache-collected, it incurs only a small performance impact and + * doesn't cause data loss. A smaller cache size might save you memory. + * + * You should choose cache sizes appropriate for storing a reasonable + * number of values rather than every value. To prevent too much recalculation, + * choose cache sizes that are at least large enough to hold memoized values for + * all hooks/queries on the screen at any given time. + */ +/* + * We assume a "base value" of 1000 here, which is already very generous. + * In most applications, it will be very unlikely that 1000 different queries + * are on screen at the same time. + */ +export interface CacheSizes { + /** + * Cache size for the [`print`](https://github.com/apollographql/apollo-client/blob/main/src/utilities/graphql/print.ts) function. + * + * It is called with transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @remarks + * This method is called to transform a GraphQL query AST parsed by `gql` + * back into a GraphQL string. + * + * @privateRemarks + * This method is called from the `QueryManager` and various `ApolloLink`s, + * always with the "serverQuery", so the server-facing part of a transformed + * `DocumentNode`. + */ + print: number; + /** + * Cache size for the [`parser`](https://github.com/apollographql/apollo-client/blob/main/src/react/parser/index.ts) function. + * + * It is called with user-provided `DocumentNode`s. + * + * @defaultValue + * Defaults to `1000`. + * + * @remarks + * This method is called by HOCs and hooks. + * + * @privateRemarks + * This function is used directly in HOCs, and nowadays mainly accessed by + * calling `verifyDocumentType` from various hooks. + * It is called with a user-provided DocumentNode. + */ + parser: number; + /** + * Cache size for the cache of [`DocumentTransform`](https://github.com/apollographql/apollo-client/blob/main/src/utilities/graphql/DocumentTransform.ts) + * instances with the `cache` option set to `true`. + * + * Can be called with user-defined or already-transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @remarks + * The cache size here should be chosen with other `DocumentTransform`s in mind. + * For example, if there was a `DocumentTransform` that would take `x` `DocumentNode`s, + * and returned a differently-transformed `DocumentNode` depending if the app is + * online or offline, then we assume that the cache returns `2*x` documents. + * If that were concatenated with another `DocumentTransform` that would + * also duplicate the cache size, you'd need to account for `4*x` documents + * returned by the second transform. + * + * Due to an implementation detail of Apollo Client, if you use custom document + * transforms you should always add `n` (the "base" number of user-provided + * Documents) to the resulting cache size. + * + * If we assume that the user-provided transforms receive `n` documents and + * return `n` documents, the cache size should be `2*n`. + * + * If we assume that the chain of user-provided transforms receive `n` documents and + * return `4*n` documents, the cache size should be `5*n`. + * + * This size should also then be used in every other cache that mentions that + * it operates on a "transformed" `DocumentNode`. + * + * @privateRemarks + * Cache size for the `performWork` method of each [`DocumentTransform`](https://github.com/apollographql/apollo-client/blob/main/src/utilities/graphql/DocumentTransform.ts). + * + * No user-provided DocumentNode will actually be "the last one", as we run the + * `defaultDocumentTransform` before *and* after the user-provided transforms. + * For that reason, we need the extra `n` here - `n` for "before transformation" + * plus the actual maximum cache size of the user-provided transform chain. + * + * This method is called from `transformDocument`, which is called from + * `QueryManager` with a user-provided DocumentNode. + * It is also called with already-transformed DocumentNodes, assuming the + * user provided additional transforms. + * + */ + "documentTransform.cache": number; + /** + * A cache inside of [`QueryManager`](https://github.com/apollographql/apollo-client/blob/main/src/core/QueryManager.ts). + * + * It is called with transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @privateRemarks + * Cache size for the `transformCache` used in the `getDocumentInfo` method of `QueryManager`. + * Called throughout the `QueryManager` with transformed DocumentNodes. + */ + "queryManager.getDocumentInfo": number; + /** + * A cache inside of [`PersistedQueryLink`](https://github.com/apollographql/apollo-client/blob/main/src/link/persisted-queries/index.ts). + * + * It is called with transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @remarks + * This cache is used to cache the hashes of persisted queries. + * + * @privateRemarks + * Cache size for the `hashesByQuery` cache in the `PersistedQueryLink`. + */ + "PersistedQueryLink.persistedQueryHashes": number; + /** + * Cache used by [`canonicalStringify`](https://github.com/apollographql/apollo-client/blob/main/src/utilities/common/canonicalStringify.ts). + * + * @defaultValue + * Defaults to `1000`. + * + * @remarks + * This cache contains the sorted keys of objects that are stringified by + * `canonicalStringify`. + * It uses the stringified unsorted keys of objects as keys. + * The cache will not grow beyond the size of different object **shapes** + * encountered in an application, no matter how much actual data gets stringified. + * + * @privateRemarks + * Cache size for the `sortingMap` in `canonicalStringify`. + */ + canonicalStringify: number; + /** + * A cache inside of [`FragmentRegistry`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/fragmentRegistry.ts). + * + * Can be called with user-defined or already-transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @privateRemarks + * + * Cache size for the `transform` method of FragmentRegistry. + * This function is called as part of the `defaultDocumentTransform` which will be called with + * user-provided and already-transformed DocumentNodes. + * + */ + "fragmentRegistry.transform": number; + /** + * A cache inside of [`FragmentRegistry`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/fragmentRegistry.ts). + * + * This function is called with fragment names in the form of a string. + * + * @defaultValue + * Defaults to `1000`. + * + * @remarks + * The size of this case should be chosen with the number of fragments in + * your application in mind. + * + * Note: + * This function is a dependency of `fragmentRegistry.transform`, so having too small of a cache size here + * might involuntarily invalidate values in the `transform` cache. + * + * @privateRemarks + * Cache size for the `lookup` method of FragmentRegistry. + */ + "fragmentRegistry.lookup": number; + /** + * Cache size for the `findFragmentSpreads` method of [`FragmentRegistry`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/fragmentRegistry.ts). + * + * This function is called with transformed `DocumentNode`s, as well as recursively + * with every fragment spread referenced within that, or a fragment referenced by a + * fragment spread. + * + * @defaultValue + * Defaults to `4000`. + * + * @remarks + * + * Note: This function is a dependency of `fragmentRegistry.transform`, so having too small of cache size here + * might involuntarily invalidate values in the `transform` cache. + */ + "fragmentRegistry.findFragmentSpreads": number; + /** + * Cache size for the `getFragmentDoc` method of [`ApolloCache`](https://github.com/apollographql/apollo-client/blob/main/src/cache/core/cache.ts). + * + * This function is called with user-provided fragment definitions. + * + * @defaultValue + * Defaults to `1000`. + * + * @remarks + * This function is called from `readFragment` with user-provided fragment definitions. + */ + "cache.fragmentQueryDocuments": number; + /** + * Cache used in [`removeTypenameFromVariables`](https://github.com/apollographql/apollo-client/blob/main/src/link/remove-typename/removeTypenameFromVariables.ts). + * + * This function is called transformed `DocumentNode`s. + * + * @defaultValue + * Defaults to `2000`. + * + * @privateRemarks + * Cache size for the `getVariableDefinitions` function of `removeTypenameFromVariables`. + */ + "removeTypenameFromVariables.getVariableDefinitions": number; + /** + * Cache size for the `maybeBroadcastWatch` method on [`InMemoryCache`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/inMemoryCache.ts). + * + * Note: `maybeBroadcastWatch` will be set to the `resultCacheMaxSize` option and + * will fall back to this configuration value if the option is not set. + * + * @defaultValue + * Defaults to `5000`. + * + * @remarks + * This method is used for dependency tracking in the `InMemoryCache` and + * prevents from unnecessary re-renders. + * It is recommended to keep this value significantly higher than the number of + * possible subscribers you will have active at the same time in your application + * at any time. + */ + "inMemoryCache.maybeBroadcastWatch": number; + /** + * Cache size for the `executeSelectionSet` method on [`StoreReader`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/readFromStore.ts). + * + * Note: + * `executeSelectionSet` will be set to the `resultCacheMaxSize` option and + * will fall back to this configuration value if the option is not set. + * + * @defaultValue + * Defaults to `10000`. + * + * @remarks + * Every object that is read from the cache will be cached here, so it is + * recommended to set this to a high value. + */ + "inMemoryCache.executeSelectionSet": number; + /** + * Cache size for the `executeSubSelectedArray` method on [`StoreReader`](https://github.com/apollographql/apollo-client/blob/main/src/cache/inmemory/readFromStore.ts). + * + * Note: + * `executeSubSelectedArray` will be set to the `resultCacheMaxSize` option and + * will fall back to this configuration value if the option is not set. + * + * @defaultValue + * Defaults to `5000`. + * + * @remarks + * Every array that is read from the cache will be cached here, so it is + * recommended to set this to a high value. + */ + "inMemoryCache.executeSubSelectedArray": number; +} + +const cacheSizeSymbol = Symbol.for("apollo.cacheSize"); +/** + * + * The global cache size configuration for Apollo Client. + * + * @remarks + * + * You can directly modify this object, but any modification will + * only have an effect on caches that are created after the modification. + * + * So for global caches, such as `parser`, `canonicalStringify` and `print`, + * you might need to call `.reset` on them, which will essentially re-create them. + * + * Alternatively, you can set `globalThis[Symbol.for("apollo.cacheSize")]` before + * you load the Apollo Client package: + * + * @example + * ```ts + * globalThis[Symbol.for("apollo.cacheSize")] = { + * parser: 100 + * } satisfies Partial // the `satisfies` is optional if using TypeScript + * ``` + */ +export const cacheSizes: Partial = { ...global[cacheSizeSymbol] }; + +export const enum defaultCacheSizes { + parser = 1000, + canonicalStringify = 1000, + print = 2000, + "documentTransform.cache" = 2000, + "queryManager.getDocumentInfo" = 2000, + "PersistedQueryLink.persistedQueryHashes" = 2000, + "fragmentRegistry.transform" = 2000, + "fragmentRegistry.lookup" = 1000, + "fragmentRegistry.findFragmentSpreads" = 4000, + "cache.fragmentQueryDocuments" = 1000, + "removeTypenameFromVariables.getVariableDefinitions" = 2000, + "inMemoryCache.maybeBroadcastWatch" = 5000, + "inMemoryCache.executeSelectionSet" = 50000, + "inMemoryCache.executeSubSelectedArray" = 10000, +} diff --git a/src/utilities/common/__tests__/canonicalStringify.ts b/src/utilities/common/__tests__/canonicalStringify.ts new file mode 100644 index 00000000000..a82f18e3863 --- /dev/null +++ b/src/utilities/common/__tests__/canonicalStringify.ts @@ -0,0 +1,110 @@ +import { canonicalStringify } from "../canonicalStringify"; + +function forEachPermutation( + keys: string[], + callback: (permutation: string[]) => void +) { + if (keys.length <= 1) { + callback(keys); + return; + } + const first = keys[0]; + const rest = keys.slice(1); + forEachPermutation(rest, (permutation) => { + for (let i = 0; i <= permutation.length; ++i) { + callback([...permutation.slice(0, i), first, ...permutation.slice(i)]); + } + }); +} + +function allObjectPermutations>(obj: T) { + const keys = Object.keys(obj); + const permutations: T[] = []; + forEachPermutation(keys, (permutation) => { + const permutationObj = Object.create(Object.getPrototypeOf(obj)); + permutation.forEach((key) => { + permutationObj[key] = obj[key]; + }); + permutations.push(permutationObj); + }); + return permutations; +} + +describe("canonicalStringify", () => { + beforeEach(() => { + canonicalStringify.reset(); + }); + + it("should not modify original object", () => { + const obj = { c: 3, a: 1, b: 2 }; + expect(canonicalStringify(obj)).toBe('{"a":1,"b":2,"c":3}'); + expect(Object.keys(obj)).toEqual(["c", "a", "b"]); + }); + + it("forEachPermutation should work", () => { + const permutations: string[][] = []; + forEachPermutation(["a", "b", "c"], (permutation) => { + permutations.push(permutation); + }); + expect(permutations).toEqual([ + ["a", "b", "c"], + ["b", "a", "c"], + ["b", "c", "a"], + ["a", "c", "b"], + ["c", "a", "b"], + ["c", "b", "a"], + ]); + }); + + it("canonicalStringify should stably stringify all permutations of an object", () => { + const unstableStrings = new Set(); + const stableStrings = new Set(); + + allObjectPermutations({ + c: 3, + a: 1, + b: 2, + }).forEach((obj) => { + unstableStrings.add(JSON.stringify(obj)); + stableStrings.add(canonicalStringify(obj)); + + expect(canonicalStringify(obj)).toBe('{"a":1,"b":2,"c":3}'); + + allObjectPermutations({ + z: "z", + y: ["y", obj, "why"], + x: "x", + }).forEach((parent) => { + expect(canonicalStringify(parent)).toBe( + '{"x":"x","y":["y",{"a":1,"b":2,"c":3},"why"],"z":"z"}' + ); + }); + }); + + expect(unstableStrings.size).toBe(6); + expect(stableStrings.size).toBe(1); + }); + + it("should not modify keys of custom-prototype objects", () => { + class Custom { + z = "z"; + y = "y"; + x = "x"; + b = "b"; + a = "a"; + c = "c"; + } + + const obj = { + z: "z", + x: "x", + y: new Custom(), + }; + + expect(Object.keys(obj.y)).toEqual(["z", "y", "x", "b", "a", "c"]); + + expect(canonicalStringify(obj)).toBe( + '{"x":"x","y":{"z":"z","y":"y","x":"x","b":"b","a":"a","c":"c"},"z":"z"}' + ); + }); +}); diff --git a/src/utilities/common/__tests__/mergeDeep.ts b/src/utilities/common/__tests__/mergeDeep.ts index fbf4ec16eb1..fcdc680e720 100644 --- a/src/utilities/common/__tests__/mergeDeep.ts +++ b/src/utilities/common/__tests__/mergeDeep.ts @@ -146,7 +146,7 @@ describe("mergeDeep", function () { }); it("supports custom reconciler functions", function () { - const merger = new DeepMerger((target, source, key) => { + const merger = new DeepMerger(function (target, source, key) { const targetValue = target[key]; const sourceValue = source[key]; if (Array.isArray(sourceValue)) { diff --git a/src/utilities/common/canUse.ts b/src/utilities/common/canUse.ts index 72e56c70388..217cc01158b 100644 --- a/src/utilities/common/canUse.ts +++ b/src/utilities/common/canUse.ts @@ -2,7 +2,9 @@ import { maybe } from "../globals/index.js"; export const canUseWeakMap = typeof WeakMap === "function" && - maybe(() => navigator.product) !== "ReactNative"; + !maybe( + () => navigator.product == "ReactNative" && !(global as any).HermesInternal + ); export const canUseWeakSet = typeof WeakSet === "function"; diff --git a/src/utilities/common/canonicalStringify.ts b/src/utilities/common/canonicalStringify.ts new file mode 100644 index 00000000000..adcc9898211 --- /dev/null +++ b/src/utilities/common/canonicalStringify.ts @@ -0,0 +1,100 @@ +import { + AutoCleanedStrongCache, + cacheSizes, + defaultCacheSizes, +} from "../../utilities/caching/index.js"; +import { registerGlobalCache } from "../caching/getMemoryInternals.js"; + +/** + * Like JSON.stringify, but with object keys always sorted in the same order. + * + * To achieve performant sorting, this function uses a Map from JSON-serialized + * arrays of keys (in any order) to sorted arrays of the same keys, with a + * single sorted array reference shared by all permutations of the keys. + * + * As a drawback, this function will add a little bit more memory for every + * object encountered that has different (more, less, a different order of) keys + * than in the past. + * + * In a typical application, this extra memory usage should not play a + * significant role, as `canonicalStringify` will be called for only a limited + * number of object shapes, and the cache will not grow beyond a certain point. + * But in some edge cases, this could be a problem, so we provide + * canonicalStringify.reset() as a way of clearing the cache. + * */ +export const canonicalStringify = Object.assign( + function canonicalStringify(value: any): string { + return JSON.stringify(value, stableObjectReplacer); + }, + { + reset() { + // Clearing the sortingMap will reclaim all cached memory, without + // affecting the logical results of canonicalStringify, but potentially + // sacrificing performance until the cache is refilled. + sortingMap = new AutoCleanedStrongCache( + cacheSizes.canonicalStringify || defaultCacheSizes.canonicalStringify + ); + }, + } +); + +if (__DEV__) { + registerGlobalCache("canonicalStringify", () => sortingMap.size); +} + +// Values are JSON-serialized arrays of object keys (in any order), and values +// are sorted arrays of the same keys. +let sortingMap!: AutoCleanedStrongCache; +canonicalStringify.reset(); + +// The JSON.stringify function takes an optional second argument called a +// replacer function. This function is called for each key-value pair in the +// object being stringified, and its return value is used instead of the +// original value. If the replacer function returns a new value, that value is +// stringified as JSON instead of the original value of the property. +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter +function stableObjectReplacer(key: string, value: any) { + if (value && typeof value === "object") { + const proto = Object.getPrototypeOf(value); + // We don't want to mess with objects that are not "plain" objects, which + // means their prototype is either Object.prototype or null. This check also + // prevents needlessly rearranging the indices of arrays. + if (proto === Object.prototype || proto === null) { + const keys = Object.keys(value); + // If keys is already sorted, let JSON.stringify serialize the original + // value instead of creating a new object with keys in the same order. + if (keys.every(everyKeyInOrder)) return value; + const unsortedKey = JSON.stringify(keys); + let sortedKeys = sortingMap.get(unsortedKey); + if (!sortedKeys) { + keys.sort(); + const sortedKey = JSON.stringify(keys); + // Checking for sortedKey in the sortingMap allows us to share the same + // sorted array reference for all permutations of the same set of keys. + sortedKeys = sortingMap.get(sortedKey) || keys; + sortingMap.set(unsortedKey, sortedKeys); + sortingMap.set(sortedKey, sortedKeys); + } + const sortedObject = Object.create(proto); + // Reassigning the keys in sorted order will cause JSON.stringify to + // serialize them in sorted order. + sortedKeys.forEach((key) => { + sortedObject[key] = value[key]; + }); + return sortedObject; + } + } + return value; +} + +// Since everything that happens in stableObjectReplacer benefits from being as +// efficient as possible, we use a static function as the callback for +// keys.every in order to test if the provided keys are already sorted without +// allocating extra memory for a callback. +function everyKeyInOrder( + key: string, + i: number, + keys: readonly string[] +): boolean { + return i === 0 || keys[i - 1] <= key; +} diff --git a/src/utilities/common/errorHandling.ts b/src/utilities/common/errorHandling.ts index 653a0c12cad..77e4877129a 100644 --- a/src/utilities/common/errorHandling.ts +++ b/src/utilities/common/errorHandling.ts @@ -8,9 +8,8 @@ export function graphQLResultHasError(result: FetchResult): boolean { } export function getGraphQLErrorsFromResult(result: FetchResult) { - const graphQLErrors = isNonEmptyArray(result.errors) - ? result.errors.slice(0) - : []; + const graphQLErrors = + isNonEmptyArray(result.errors) ? result.errors.slice(0) : []; if ( isExecutionPatchIncrementalResult(result) && diff --git a/src/utilities/common/filterInPlace.ts b/src/utilities/common/filterInPlace.ts deleted file mode 100644 index cafeed316f2..00000000000 --- a/src/utilities/common/filterInPlace.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function filterInPlace( - array: T[], - test: (elem: T) => boolean, - context?: any -): T[] { - let target = 0; - array.forEach(function (elem, i) { - if (test.call(this, elem, i, array)) { - array[target++] = elem; - } - }, context); - array.length = target; - return array; -} diff --git a/src/utilities/common/mergeDeep.ts b/src/utilities/common/mergeDeep.ts index a2171f9f023..ded03c3786e 100644 --- a/src/utilities/common/mergeDeep.ts +++ b/src/utilities/common/mergeDeep.ts @@ -16,18 +16,13 @@ const { hasOwnProperty } = Object.prototype; // inference, but that approach has several fatal flaws (boolean becomes // true & false, and the inferred type ends up as unknown in many cases), // in addition to being nearly impossible to explain/understand. -export type TupleToIntersection = T extends [infer A] - ? A - : T extends [infer A, infer B] - ? A & B - : T extends [infer A, infer B, infer C] - ? A & B & C - : T extends [infer A, infer B, infer C, infer D] - ? A & B & C & D - : T extends [infer A, infer B, infer C, infer D, infer E] - ? A & B & C & D & E - : T extends (infer U)[] - ? U +export type TupleToIntersection = + T extends [infer A] ? A + : T extends [infer A, infer B] ? A & B + : T extends [infer A, infer B, infer C] ? A & B & C + : T extends [infer A, infer B, infer C, infer D] ? A & B & C & D + : T extends [infer A, infer B, infer C, infer D, infer E] ? A & B & C & D & E + : T extends (infer U)[] ? U : any; export function mergeDeep( @@ -72,7 +67,7 @@ const defaultReconciler: ReconcilerFunction = function ( export class DeepMerger { constructor( - private reconciler: ReconcilerFunction = defaultReconciler + private reconciler: ReconcilerFunction = defaultReconciler as any as ReconcilerFunction ) {} public merge(target: any, source: any, ...context: TContextArgs): any { diff --git a/src/utilities/common/mergeOptions.ts b/src/utilities/common/mergeOptions.ts index 9c945f904e4..fe4eb954bbf 100644 --- a/src/utilities/common/mergeOptions.ts +++ b/src/utilities/common/mergeOptions.ts @@ -10,7 +10,7 @@ import { compact } from "./compact.js"; type OptionsUnion = | WatchQueryOptions | QueryOptions - | MutationOptions; + | MutationOptions; export function mergeOptions< TDefaultOptions extends Partial>, diff --git a/src/utilities/globals/__tests__/invariantWrappers.test.ts b/src/utilities/globals/__tests__/invariantWrappers.test.ts index 2b750c534ed..b8f51390944 100644 --- a/src/utilities/globals/__tests__/invariantWrappers.test.ts +++ b/src/utilities/globals/__tests__/invariantWrappers.test.ts @@ -1,34 +1,56 @@ -import { loadErrorMessageHandler } from "../../../dev"; -import { spyOnConsole, withCleanup } from "../../../testing/internal"; +import { spyOnConsole } from "../../../testing/internal"; import { ApolloErrorMessageHandler, InvariantError, invariant, } from "../invariantWrappers"; +function withDev() { + const originalErrorMessageHandler = window[ApolloErrorMessageHandler]; + window[ApolloErrorMessageHandler] = undefined; + let dev: typeof import("../../../dev"); + let restore = () => {}; + // we're running the test inside of `jest.isolateModulesAsync` to avoid + // the test overriding the module-level state of the `dev` module + const cleanupFinished = jest.isolateModulesAsync( + () => + new Promise((resolve) => { + dev = require("../../../dev"); + restore = resolve; + }) + ); + // replicate the code of `src/config/jest/setup.ts` + dev!.loadErrorMessageHandler(); + return { + ...dev!, + async [Symbol.asyncDispose]() { + restore(); + await cleanupFinished; + window[ApolloErrorMessageHandler] = originalErrorMessageHandler; + }, + }; +} + function disableErrorMessageHandler() { - const original = window[ApolloErrorMessageHandler]; + // eslint-disable-next-line local-rules/require-using-disposable + const dev = withDev(); delete window[ApolloErrorMessageHandler]; - return withCleanup({ original }, ({ original }) => { - window[ApolloErrorMessageHandler] = original; - }); + return dev; } function mockErrorMessageHandler() { - const original = window[ApolloErrorMessageHandler]; + // eslint-disable-next-line local-rules/require-using-disposable + const dev = withDev(); delete window[ApolloErrorMessageHandler]; - loadErrorMessageHandler({ + dev.loadErrorMessageHandler({ 5: { file: "foo", message: "Replacing %s, %d, %f, %o" }, }); - - return withCleanup({ original }, ({ original }) => { - window[ApolloErrorMessageHandler] = original; - }); + return dev; } -test("base invariant(false, 5, ...), no handlers", () => { - using _ = disableErrorMessageHandler(); +test("base invariant(false, 5, ...), no handlers", async () => { + await using _ = disableErrorMessageHandler(); expect(() => { invariant(false, 5, "string", 1, 1.1, { a: 1 }); }).toThrow( @@ -50,29 +72,47 @@ test("base invariant(false, 5, ...), no handlers", () => { ); }); -test("base invariant(false, 5, ...), handlers in place", () => { - using _ = mockErrorMessageHandler(); +test("base invariant(false, 5, ...), handlers in place", async () => { + await using _ = mockErrorMessageHandler(); expect(() => { invariant(false, 5, "string", 1, 1.1, { a: 1 }); }).toThrow(new InvariantError('Replacing string, 1, 1.1, {\n "a": 1\n}')); }); -test("base invariant(false, undefined), no handlers", () => { - using _ = disableErrorMessageHandler(); +test("base invariant(false, 5, ...), custom handler gets passed arguments", async () => { + await using dev = disableErrorMessageHandler(); + + const handler = jest.fn(() => ""); + dev.setErrorMessageHandler(handler); + + try { + invariant(false, 5, "string", 1, 1.1, { a: 1 }); + } catch {} + + expect(handler).toHaveBeenCalledWith(5, [ + "string", + "1", + "1.1", + '{\n "a": 1\n}', + ]); +}); + +test("base invariant(false, undefined), no handlers", async () => { + await using _ = disableErrorMessageHandler(); expect(() => { invariant(false); }).toThrow(new InvariantError("Invariant Violation")); }); -test("base invariant(false, undefined), handlers in place", () => { - using _ = mockErrorMessageHandler(); +test("base invariant(false, undefined), handlers in place", async () => { + await using _ = mockErrorMessageHandler(); expect(() => { invariant(false); }).toThrow(new InvariantError("Invariant Violation")); }); -test("invariant.log(5, ...), no handlers", () => { - using _ = disableErrorMessageHandler(); +test("invariant.log(5, ...), no handlers", async () => { + await using _ = disableErrorMessageHandler(); using consoleSpy = spyOnConsole("log"); invariant.log(5, "string", 1, 1.1, { a: 1 }); expect(consoleSpy.log).toHaveBeenCalledWith( @@ -87,8 +127,8 @@ test("invariant.log(5, ...), no handlers", () => { ); }); -test("invariant.log(5, ...), with handlers", () => { - using _ = mockErrorMessageHandler(); +test("invariant.log(5, ...), with handlers", async () => { + await using _ = mockErrorMessageHandler(); using consoleSpy = spyOnConsole("log"); invariant.log(5, "string", 1, 1.1, { a: 1 }); expect(consoleSpy.log).toHaveBeenCalledWith( @@ -100,8 +140,22 @@ test("invariant.log(5, ...), with handlers", () => { ); }); -test("base invariant(false, 6, ...), raises fallback", () => { - using _ = mockErrorMessageHandler(); +test("invariant.log(5, ...), custom handler does not get passed arguments", async () => { + await using dev = disableErrorMessageHandler(); + using _consoleSpy = spyOnConsole("log"); + + const handler = jest.fn(() => ""); + dev.setErrorMessageHandler(handler); + + try { + invariant.log(5, "string", 1, 1.1, { a: 1 }); + } catch {} + + expect(handler).toHaveBeenCalledWith(5, []); +}); + +test("base invariant(false, 6, ...), raises fallback", async () => { + await using _ = mockErrorMessageHandler(); expect(() => { invariant(false, 6, "hello"); }).toThrow( diff --git a/src/utilities/globals/invariantWrappers.ts b/src/utilities/globals/invariantWrappers.ts index adcbea0ad01..81070cbbb7d 100644 --- a/src/utilities/globals/invariantWrappers.ts +++ b/src/utilities/globals/invariantWrappers.ts @@ -111,15 +111,15 @@ const ApolloErrorMessageHandler = Symbol.for( declare global { interface Window { [ApolloErrorMessageHandler]?: { - (message: string | number, args: unknown[]): string | undefined; + (message: string | number, args: string[]): string | undefined; } & ErrorCodes; } } function stringify(arg: any) { - return typeof arg == "string" - ? arg - : stringifyForDisplay(arg, 2).slice(0, 1000); + return typeof arg == "string" ? arg : ( + stringifyForDisplay(arg, 2).slice(0, 1000) + ); } function getHandledErrorMsg( diff --git a/src/utilities/graphql/DocumentTransform.ts b/src/utilities/graphql/DocumentTransform.ts index 0a614df4190..ec6e0e44152 100644 --- a/src/utilities/graphql/DocumentTransform.ts +++ b/src/utilities/graphql/DocumentTransform.ts @@ -3,13 +3,28 @@ import { canUseWeakMap, canUseWeakSet } from "../common/canUse.js"; import { checkDocument } from "./getFromAST.js"; import { invariant } from "../globals/index.js"; import type { DocumentNode } from "graphql"; +import { WeakCache } from "@wry/caches"; +import { wrap } from "optimism"; +import { cacheSizes } from "../caching/index.js"; export type DocumentTransformCacheKey = ReadonlyArray; type TransformFn = (document: DocumentNode) => DocumentNode; interface DocumentTransformOptions { + /** + * Determines whether to cache the transformed GraphQL document. Caching can speed up repeated calls to the document transform for the same input document. Set to `false` to completely disable caching for the document transform. When disabled, this option takes precedence over the [`getCacheKey`](#getcachekey) option. + * + * The default value is `true`. + */ cache?: boolean; + /** + * Defines a custom cache key for a GraphQL document that will determine whether to re-run the document transform when given the same input GraphQL document. Returns an array that defines the cache key. Return `undefined` to disable caching for that GraphQL document. + * + * > **Note:** The items in the array may be any type, but also need to be referentially stable to guarantee a stable cache key. + * + * The default implementation of this function returns the `document` as the cache key. + */ getCacheKey?: ( document: DocumentNode ) => DocumentTransformCacheKey | undefined; @@ -21,14 +36,10 @@ function identity(document: DocumentNode) { export class DocumentTransform { private readonly transform: TransformFn; + private cached: boolean; - private readonly resultCache = canUseWeakSet - ? new WeakSet() - : new Set(); - - private stableCacheKeys: - | Trie<{ key: DocumentTransformCacheKey; value?: DocumentNode }> - | undefined; + private readonly resultCache = + canUseWeakSet ? new WeakSet() : new Set(); // This default implementation of getCacheKey can be overridden by providing // options.getCacheKey to the DocumentTransform constructor. In general, a @@ -53,14 +64,17 @@ export class DocumentTransform { left: DocumentTransform, right: DocumentTransform = DocumentTransform.identity() ) { - return new DocumentTransform( - (document) => { - const documentTransform = predicate(document) ? left : right; - - return documentTransform.transformDocument(document); - }, - // Reasonably assume both `left` and `right` transforms handle their own caching - { cache: false } + return Object.assign( + new DocumentTransform( + (document) => { + const documentTransform = predicate(document) ? left : right; + + return documentTransform.transformDocument(document); + }, + // Reasonably assume both `left` and `right` transforms handle their own caching + { cache: false } + ), + { left, right } ); } @@ -74,12 +88,42 @@ export class DocumentTransform { // Override default `getCacheKey` function, which returns [document]. this.getCacheKey = options.getCacheKey; } + this.cached = options.cache !== false; + + this.resetCache(); + } - if (options.cache !== false) { - this.stableCacheKeys = new Trie(canUseWeakMap, (key) => ({ key })); + /** + * Resets the internal cache of this transform, if it has one. + */ + resetCache() { + if (this.cached) { + const stableCacheKeys = new Trie(canUseWeakMap); + this.performWork = wrap( + DocumentTransform.prototype.performWork.bind(this), + { + makeCacheKey: (document) => { + const cacheKeys = this.getCacheKey(document); + if (cacheKeys) { + invariant( + Array.isArray(cacheKeys), + "`getCacheKey` must return an array or undefined" + ); + return stableCacheKeys.lookupArray(cacheKeys); + } + }, + max: cacheSizes["documentTransform.cache"], + cache: WeakCache, + } + ); } } + private performWork(document: DocumentNode) { + checkDocument(document); + return this.transform(document); + } + transformDocument(document: DocumentNode) { // If a user passes an already transformed result back to this function, // immediately return it. @@ -87,46 +131,39 @@ export class DocumentTransform { return document; } - const cacheEntry = this.getStableCacheEntry(document); - - if (cacheEntry && cacheEntry.value) { - return cacheEntry.value; - } - - checkDocument(document); - - const transformedDocument = this.transform(document); + const transformedDocument = this.performWork(document); this.resultCache.add(transformedDocument); - if (cacheEntry) { - cacheEntry.value = transformedDocument; - } - return transformedDocument; } - concat(otherTransform: DocumentTransform) { - return new DocumentTransform( - (document) => { - return otherTransform.transformDocument( - this.transformDocument(document) - ); - }, - // Reasonably assume both transforms handle their own caching - { cache: false } + concat(otherTransform: DocumentTransform): DocumentTransform { + return Object.assign( + new DocumentTransform( + (document) => { + return otherTransform.transformDocument( + this.transformDocument(document) + ); + }, + // Reasonably assume both transforms handle their own caching + { cache: false } + ), + { + left: this, + right: otherTransform, + } ); } - getStableCacheEntry(document: DocumentNode) { - if (!this.stableCacheKeys) return; - const cacheKeys = this.getCacheKey(document); - if (cacheKeys) { - invariant( - Array.isArray(cacheKeys), - "`getCacheKey` must return an array or undefined" - ); - return this.stableCacheKeys.lookupArray(cacheKeys); - } - } + /** + * @internal + * Used to iterate through all transforms that are concatenations or `split` links. + */ + readonly left?: DocumentTransform; + /** + * @internal + * Used to iterate through all transforms that are concatenations or `split` links. + */ + readonly right?: DocumentTransform; } diff --git a/src/utilities/graphql/__tests__/DocumentTransform.ts b/src/utilities/graphql/__tests__/DocumentTransform.ts index 5f96ffa4e21..b35c8e0ef08 100644 --- a/src/utilities/graphql/__tests__/DocumentTransform.ts +++ b/src/utilities/graphql/__tests__/DocumentTransform.ts @@ -162,8 +162,8 @@ test("allows custom cache keys to be defined", () => { ); const transform = jest.fn((document: DocumentNode) => { - return online - ? onlineTransform.transformDocument(document) + return online ? + onlineTransform.transformDocument(document) : offlineTransform.transformDocument(document); }); diff --git a/src/utilities/graphql/print.ts b/src/utilities/graphql/print.ts index 5fb1cb68599..20e779a9a55 100644 --- a/src/utilities/graphql/print.ts +++ b/src/utilities/graphql/print.ts @@ -1,14 +1,33 @@ +import type { ASTNode } from "graphql"; import { print as origPrint } from "graphql"; -import { canUseWeakMap } from "../common/canUse.js"; +import { + AutoCleanedWeakCache, + cacheSizes, + defaultCacheSizes, +} from "../caching/index.js"; +import { registerGlobalCache } from "../caching/getMemoryInternals.js"; -const printCache = canUseWeakMap ? new WeakMap() : undefined; -export const print: typeof origPrint = (ast) => { - let result; - result = printCache?.get(ast); +let printCache!: AutoCleanedWeakCache; +export const print = Object.assign( + (ast: ASTNode) => { + let result = printCache.get(ast); - if (!result) { - result = origPrint(ast); - printCache?.set(ast, result); + if (!result) { + result = origPrint(ast); + printCache.set(ast, result); + } + return result; + }, + { + reset() { + printCache = new AutoCleanedWeakCache( + cacheSizes.print || defaultCacheSizes.print + ); + }, } - return result; -}; +); +print.reset(); + +if (__DEV__) { + registerGlobalCache("print", () => (printCache ? printCache.size : 0)); +} diff --git a/src/utilities/graphql/storeUtils.ts b/src/utilities/graphql/storeUtils.ts index 439a47359ea..c96e0628eb8 100644 --- a/src/utilities/graphql/storeUtils.ts +++ b/src/utilities/graphql/storeUtils.ts @@ -24,6 +24,7 @@ import type { import { isNonNullObject } from "../common/objects.js"; import type { FragmentMap } from "./fragments.js"; import { getFragmentFromSelection } from "./fragments.js"; +import { canonicalStringify } from "../common/canonicalStringify.js"; export interface Reference { readonly __ref: string; @@ -212,6 +213,11 @@ const KNOWN_DIRECTIVES: string[] = [ "nonreactive", ]; +// Default stable JSON.stringify implementation used by getStoreKeyName. Can be +// updated/replaced with something better by calling +// getStoreKeyName.setStringify(newStringifyFunction). +let storeKeyNameStringify: (value: any) => string = canonicalStringify; + export const getStoreKeyName = Object.assign( function ( fieldName: string, @@ -228,8 +234,9 @@ export const getStoreKeyName = Object.assign( directives["connection"]["filter"] && (directives["connection"]["filter"] as string[]).length > 0 ) { - const filterKeys = directives["connection"]["filter"] - ? (directives["connection"]["filter"] as string[]) + const filterKeys = + directives["connection"]["filter"] ? + (directives["connection"]["filter"] as string[]) : []; filterKeys.sort(); @@ -238,7 +245,9 @@ export const getStoreKeyName = Object.assign( filteredArgs[key] = args[key]; }); - return `${directives["connection"]["key"]}(${stringify(filteredArgs)})`; + return `${directives["connection"]["key"]}(${storeKeyNameStringify( + filteredArgs + )})`; } else { return directives["connection"]["key"]; } @@ -250,7 +259,7 @@ export const getStoreKeyName = Object.assign( // We can't use `JSON.stringify` here since it's non-deterministic, // and can lead to different store key names being created even though // the `args` object used during creation has the same properties/values. - const stringifiedArgs: string = stringify(args); + const stringifiedArgs: string = storeKeyNameStringify(args); completeFieldName += `(${stringifiedArgs})`; } @@ -258,7 +267,9 @@ export const getStoreKeyName = Object.assign( Object.keys(directives).forEach((key) => { if (KNOWN_DIRECTIVES.indexOf(key) !== -1) return; if (directives[key] && Object.keys(directives[key]).length) { - completeFieldName += `@${key}(${stringify(directives[key])})`; + completeFieldName += `@${key}(${storeKeyNameStringify( + directives[key] + )})`; } else { completeFieldName += `@${key}`; } @@ -268,35 +279,14 @@ export const getStoreKeyName = Object.assign( return completeFieldName; }, { - setStringify(s: typeof stringify) { - const previous = stringify; - stringify = s; + setStringify(s: typeof storeKeyNameStringify) { + const previous = storeKeyNameStringify; + storeKeyNameStringify = s; return previous; }, } ); -// Default stable JSON.stringify implementation. Can be updated/replaced with -// something better by calling getStoreKeyName.setStringify. -let stringify = function defaultStringify(value: any): string { - return JSON.stringify(value, stringifyReplacer); -}; - -function stringifyReplacer(_key: string, value: any): any { - if (isNonNullObject(value) && !Array.isArray(value)) { - value = Object.keys(value) - .sort() - .reduce( - (copy, key) => { - copy[key] = value[key]; - return copy; - }, - {} as Record - ); - } - return value; -} - export function argumentsObjectFromField( field: FieldNode | DirectiveNode, variables?: Record diff --git a/src/utilities/graphql/transform.ts b/src/utilities/graphql/transform.ts index 7ff844ad150..7a424f4df20 100644 --- a/src/utilities/graphql/transform.ts +++ b/src/utilities/graphql/transform.ts @@ -12,7 +12,7 @@ import type { FragmentSpreadNode, VariableDefinitionNode, ASTNode, - ASTVisitor, + ASTVisitFn, InlineFragmentNode, } from "graphql"; import { visit, Kind } from "graphql"; @@ -29,6 +29,12 @@ import type { FragmentMap } from "./fragments.js"; import { createFragmentMap } from "./fragments.js"; import { isArray, isNonEmptyArray } from "../common/arrays.js"; +// https://github.com/graphql/graphql-js/blob/8d7c8fccf5a9846a50785de04abda58a7eb13fc0/src/language/visitor.ts#L20-L23 +interface EnterLeaveVisitor { + readonly enter?: ASTVisitFn; + readonly leave?: ASTVisitFn; +} + export type RemoveNodeConfig = { name?: string; test?: (node: N) => boolean; @@ -73,11 +79,13 @@ function isEmpty( } function nullIfDocIsEmpty(doc: DocumentNode) { - return isEmpty( - getOperationDefinition(doc) || getFragmentDefinition(doc), - createFragmentMap(getFragmentDefinitions(doc)) - ) - ? null + return ( + isEmpty( + getOperationDefinition(doc) || getFragmentDefinition(doc), + createFragmentMap(getFragmentDefinitions(doc)) + ) + ) ? + null : doc; } @@ -208,8 +216,10 @@ export function removeDirectivesFromDocument( // original doc immediately without any modifications. let firstVisitMadeChanges = false; - const fieldOrInlineFragmentVisitor: ASTVisitor = { - enter(node: FieldNode | InlineFragmentNode) { + const fieldOrInlineFragmentVisitor: EnterLeaveVisitor< + FieldNode | InlineFragmentNode + > = { + enter(node) { if (shouldRemoveField(node.directives)) { firstVisitMadeChanges = true; return null; @@ -385,8 +395,10 @@ export function removeDirectivesFromDocument( ) ); - const enterVisitor: ASTVisitor = { - enter(node: FragmentSpreadNode | FragmentDefinitionNode) { + const enterVisitor: EnterLeaveVisitor< + FragmentSpreadNode | FragmentDefinitionNode + > = { + enter(node) { if (fragmentWillBeRemoved(node.name.value)) { return null; } @@ -588,8 +600,9 @@ export function removeArgumentsFromDocument( return { ...node, // Remove matching top level variables definitions. - variableDefinitions: node.variableDefinitions - ? node.variableDefinitions.filter( + variableDefinitions: + node.variableDefinitions ? + node.variableDefinitions.filter( (varDef) => !config.some( (arg) => arg.name === varDef.variable.name.value diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 5f120fd4290..637ae100af7 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -98,6 +98,7 @@ export type { } from "./observables/Observable.js"; export { Observable } from "./observables/Observable.js"; +export type { PromiseWithState } from "./promises/decoration.js"; export { isStatefulPromise, createFulfilledPromise, @@ -122,9 +123,19 @@ export * from "./common/stringifyForDisplay.js"; export * from "./common/mergeOptions.js"; export * from "./common/incrementalResult.js"; +export { canonicalStringify } from "./common/canonicalStringify.js"; export { omitDeep } from "./common/omitDeep.js"; export { stripTypename } from "./common/stripTypename.js"; export * from "./types/IsStrictlyAny.js"; export type { DeepOmit } from "./types/DeepOmit.js"; export type { DeepPartial } from "./types/DeepPartial.js"; +export type { OnlyRequiredProperties } from "./types/OnlyRequiredProperties.js"; + +export { + AutoCleanedStrongCache, + AutoCleanedWeakCache, + cacheSizes, + defaultCacheSizes, +} from "./caching/index.js"; +export type { CacheSizes } from "./caching/index.js"; diff --git a/src/utilities/observables/Concast.ts b/src/utilities/observables/Concast.ts index e6df7e19882..73c36520f8b 100644 --- a/src/utilities/observables/Concast.ts +++ b/src/utilities/observables/Concast.ts @@ -147,9 +147,9 @@ export class Concast extends Observable { // Any Concast object can be trivially converted to a Promise, without // having to create a new wrapper Observable. This promise provides an // easy way to observe the final state of the Concast. - private resolve: (result?: T | PromiseLike) => void; - private reject: (reason: any) => void; - public readonly promise = new Promise((resolve, reject) => { + private resolve!: (result?: T | PromiseLike) => void; + private reject!: (reason: any) => void; + public readonly promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); @@ -210,7 +210,10 @@ export class Concast extends Observable { // followed by a 'complete' message (see addObserver). iterateObserversSafely(this.observers, "complete"); } else if (isPromiseLike(value)) { - value.then((obs) => (this.sub = obs.subscribe(this.handlers))); + value.then( + (obs) => (this.sub = obs.subscribe(this.handlers)), + this.handlers.error + ); } else { this.sub = value.subscribe(this.handlers); } diff --git a/src/utilities/observables/Observable.ts b/src/utilities/observables/Observable.ts index 04fd25cac30..5ee33965f30 100644 --- a/src/utilities/observables/Observable.ts +++ b/src/utilities/observables/Observable.ts @@ -17,6 +17,7 @@ export type { Observer, ObservableSubscription, Subscriber }; const { prototype } = Observable; const fakeObsSymbol = "@@observable" as keyof typeof prototype; if (!prototype[fakeObsSymbol]) { + // @ts-expect-error prototype[fakeObsSymbol] = function () { return this; }; diff --git a/src/utilities/observables/__tests__/Concast.ts b/src/utilities/observables/__tests__/Concast.ts index d6ce248159f..b590cde2fb8 100644 --- a/src/utilities/observables/__tests__/Concast.ts +++ b/src/utilities/observables/__tests__/Concast.ts @@ -1,5 +1,5 @@ import { itAsync } from "../../../testing/core"; -import { Observable } from "../Observable"; +import { Observable, Observer } from "../Observable"; import { Concast, ConcastSourcesIterable } from "../Concast"; describe("Concast Observable (similar to Behavior Subject in RxJS)", () => { @@ -187,4 +187,115 @@ describe("Concast Observable (similar to Behavior Subject in RxJS)", () => { sub.unsubscribe(); }); }); + + it("resolving all sources of a concast frees all observer references on `this.observers`", async () => { + const { promise, resolve } = deferred>(); + const observers: Observer[] = [{ next() {} }]; + const observerRefs = observers.map((observer) => new WeakRef(observer)); + + const concast = new Concast([Observable.of(1, 2), promise]); + + concast.subscribe(observers[0]); + delete observers[0]; + + expect(concast["observers"].size).toBe(1); + + resolve(Observable.of(3, 4)); + + await expect(concast.promise).resolves.toBe(4); + + await expect(observerRefs[0]).toBeGarbageCollected(); + }); + + it("rejecting a source-wrapping promise of a concast frees all observer references on `this.observers`", async () => { + const { promise, reject } = deferred>(); + let subscribingObserver: Observer | undefined = { + next() {}, + error() {}, + }; + const subscribingObserverRef = new WeakRef(subscribingObserver); + + const concast = new Concast([ + Observable.of(1, 2), + promise, + // just to ensure this also works if the cancelling source is not the last source + Observable.of(3, 5), + ]); + + concast.subscribe(subscribingObserver); + + expect(concast["observers"].size).toBe(1); + + reject("error"); + await expect(concast.promise).rejects.toBe("error"); + subscribingObserver = undefined; + await expect(subscribingObserverRef).toBeGarbageCollected(); + }); + + it("rejecting a source of a concast frees all observer references on `this.observers`", async () => { + let subscribingObserver: Observer | undefined = { + next() {}, + error() {}, + }; + const subscribingObserverRef = new WeakRef(subscribingObserver); + + let sourceObserver!: Observer; + const sourceObservable = new Observable((o) => { + sourceObserver = o; + }); + + const concast = new Concast([ + Observable.of(1, 2), + sourceObservable, + Observable.of(3, 5), + ]); + + concast.subscribe(subscribingObserver); + + expect(concast["observers"].size).toBe(1); + + await Promise.resolve(); + sourceObserver.error!("error"); + await expect(concast.promise).rejects.toBe("error"); + subscribingObserver = undefined; + await expect(subscribingObserverRef).toBeGarbageCollected(); + }); + + it("after subscribing to an already-resolved concast, the reference is freed up again", async () => { + const concast = new Concast([Observable.of(1, 2)]); + await expect(concast.promise).resolves.toBe(2); + await Promise.resolve(); + + let sourceObserver: Observer | undefined = { next() {}, error() {} }; + const sourceObserverRef = new WeakRef(sourceObserver); + + concast.subscribe(sourceObserver); + + sourceObserver = undefined; + await expect(sourceObserverRef).toBeGarbageCollected(); + }); + + it("after subscribing to an already-rejected concast, the reference is freed up again", async () => { + const concast = new Concast([Promise.reject("error")]); + await expect(concast.promise).rejects.toBe("error"); + await Promise.resolve(); + + let sourceObserver: Observer | undefined = { next() {}, error() {} }; + const sourceObserverRef = new WeakRef(sourceObserver); + + concast.subscribe(sourceObserver); + + sourceObserver = undefined; + await expect(sourceObserverRef).toBeGarbageCollected(); + }); }); + +function deferred() { + let resolve!: (v: X) => void; + let reject!: (e: any) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { resolve, reject, promise }; +} diff --git a/src/utilities/observables/__tests__/Observable.ts b/src/utilities/observables/__tests__/Observable.ts index 47223bc0643..1729e0ae1f3 100644 --- a/src/utilities/observables/__tests__/Observable.ts +++ b/src/utilities/observables/__tests__/Observable.ts @@ -39,8 +39,13 @@ describe("Observable", () => { return constructor as any; } + type ObservableWithSub = Observable & { sub?: Subscriber }; + it("simulating super(sub) with Observable.call(this, sub)", () => { - function SubclassWithSuperCall(sub: Subscriber) { + function SubclassWithSuperCall( + this: ObservableWithSub, + sub: Subscriber + ) { const self = Observable.call(this, sub) || this; self.sub = sub; return self; @@ -49,7 +54,10 @@ describe("Observable", () => { }); it("simulating super(sub) with Observable.apply(this, arguments)", () => { - function SubclassWithSuperApplyArgs(_sub: Subscriber) { + function SubclassWithSuperApplyArgs( + this: ObservableWithSub, + _sub: Subscriber + ) { const self = Observable.apply(this, arguments) || this; self.sub = _sub; return self; @@ -58,7 +66,10 @@ describe("Observable", () => { }); it("simulating super(sub) with Observable.apply(this, [sub])", () => { - function SubclassWithSuperApplyArray(...args: [Subscriber]) { + function SubclassWithSuperApplyArray( + this: ObservableWithSub, + ...args: [Subscriber] + ) { const self = Observable.apply(this, args) || this; self.sub = args[0]; return self; diff --git a/src/utilities/observables/__tests__/asyncMap.ts b/src/utilities/observables/__tests__/asyncMap.ts index e54b2140e66..ce4227be45b 100644 --- a/src/utilities/observables/__tests__/asyncMap.ts +++ b/src/utilities/observables/__tests__/asyncMap.ts @@ -25,6 +25,7 @@ function rejectExceptions( ) { return function () { try { + // @ts-expect-error return fn.apply(this, arguments); } catch (error) { reject(error); @@ -186,17 +187,17 @@ describe("asyncMap", () => { let lastMapped = 0; const mapped = asyncMap( observable, - synchronity === "sync" - ? (n: number) => { - lastMapped = n; - if (n === 3) throw new Error("expected"); - return n * 2; - } - : async (n: number) => { - lastMapped = n; - if (n === 3) throw new Error("expected"); - return n * 2; - } + synchronity === "sync" ? + (n: number) => { + lastMapped = n; + if (n === 3) throw new Error("expected"); + return n * 2; + } + : async (n: number) => { + lastMapped = n; + if (n === 3) throw new Error("expected"); + return n * 2; + } ); const stream = new ObservableStream(mapped); await expect(stream.takeNext()).resolves.toBe(2); diff --git a/src/utilities/observables/asyncMap.ts b/src/utilities/observables/asyncMap.ts index 5bf24350a5e..dff4620d06b 100644 --- a/src/utilities/observables/asyncMap.ts +++ b/src/utilities/observables/asyncMap.ts @@ -27,9 +27,9 @@ export function asyncMap( const both = () => // If the observer is closed, we don't want to continue calling the // mapping function - it's result will be swallowed anyways. - observer.closed - ? /* will be swallowed */ (0 as any) - : examiner(arg); + observer.closed ? + /* will be swallowed */ (0 as any) + : examiner(arg); promiseQueue = promiseQueue.then(both, both).then( (result) => observer.next(result), diff --git a/src/utilities/policies/pagination.ts b/src/utilities/policies/pagination.ts index 4dd000e32da..b191995d9a0 100644 --- a/src/utilities/policies/pagination.ts +++ b/src/utilities/policies/pagination.ts @@ -42,7 +42,7 @@ export function offsetLimitPagination( // to receive any arguments, so you might prefer to throw an // exception here, instead of recovering by appending incoming // onto the existing array. - merged.push.apply(merged, incoming); + merged.push(...incoming); } } @@ -146,8 +146,9 @@ export function relayStylePagination( return existing; } - const incomingEdges = incoming.edges - ? incoming.edges.map((edge) => { + const incomingEdges = + incoming.edges ? + incoming.edges.map((edge) => { if (isReference((edge = { ...edge }))) { // In case edge is a Reference, we read out its cursor field and // store it as an extra property of the Reference object. diff --git a/src/utilities/subscriptions/relay/index.ts b/src/utilities/subscriptions/relay/index.ts new file mode 100644 index 00000000000..1c92c074fea --- /dev/null +++ b/src/utilities/subscriptions/relay/index.ts @@ -0,0 +1,60 @@ +import { Observable } from "relay-runtime"; +import type { RequestParameters, GraphQLResponse } from "relay-runtime"; +import { + handleError, + readMultipartBody, +} from "../../../link/http/parseAndCheckHttpResponse.js"; +import { maybe } from "../../index.js"; +import { serializeFetchParameter } from "../../../core/index.js"; + +import type { OperationVariables } from "../../../core/index.js"; +import type { Body } from "../../../link/http/selectHttpOptionsAndBody.js"; +import { generateOptionsForMultipartSubscription } from "../shared.js"; +import type { CreateMultipartSubscriptionOptions } from "../shared.js"; + +const backupFetch = maybe(() => fetch); + +export function createFetchMultipartSubscription( + uri: string, + { fetch: preferredFetch, headers }: CreateMultipartSubscriptionOptions = {} +) { + return function fetchMultipartSubscription( + operation: RequestParameters, + variables: OperationVariables + ): Observable { + const body: Body = { + operationName: operation.name, + variables, + query: operation.text || "", + }; + const options = generateOptionsForMultipartSubscription(headers || {}); + + return Observable.create((sink) => { + try { + options.body = serializeFetchParameter(body, "Payload"); + } catch (parseError) { + sink.error(parseError as Error); + } + + const currentFetch = preferredFetch || maybe(() => fetch) || backupFetch; + const observerNext = sink.next.bind(sink); + + currentFetch!(uri, options) + .then((response) => { + const ctype = response.headers?.get("content-type"); + + if (ctype !== null && /^multipart\/mixed/i.test(ctype)) { + return readMultipartBody(response, observerNext); + } + + sink.error(new Error("Expected multipart response")); + }) + .then(() => { + sink.complete(); + }) + .catch((err: any) => { + handleError(err, sink); + }); + }); + }; +} diff --git a/src/utilities/subscriptions/shared.ts b/src/utilities/subscriptions/shared.ts new file mode 100644 index 00000000000..f3706dab11e --- /dev/null +++ b/src/utilities/subscriptions/shared.ts @@ -0,0 +1,21 @@ +import { fallbackHttpConfig } from "../../link/http/selectHttpOptionsAndBody.js"; + +export type CreateMultipartSubscriptionOptions = { + fetch?: WindowOrWorkerGlobalScope["fetch"]; + headers?: Record; +}; + +export function generateOptionsForMultipartSubscription( + headers: Record +) { + const options: { headers: Record; body?: string } = { + ...fallbackHttpConfig.options, + headers: { + ...(headers || {}), + ...fallbackHttpConfig.headers, + accept: + "multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json", + }, + }; + return options; +} diff --git a/src/utilities/subscriptions/urql/index.ts b/src/utilities/subscriptions/urql/index.ts new file mode 100644 index 00000000000..26bf4d4fb57 --- /dev/null +++ b/src/utilities/subscriptions/urql/index.ts @@ -0,0 +1,56 @@ +import { Observable } from "../../index.js"; +import { + handleError, + readMultipartBody, +} from "../../../link/http/parseAndCheckHttpResponse.js"; +import { maybe } from "../../index.js"; +import { serializeFetchParameter } from "../../../core/index.js"; +import type { Body } from "../../../link/http/selectHttpOptionsAndBody.js"; +import { generateOptionsForMultipartSubscription } from "../shared.js"; +import type { CreateMultipartSubscriptionOptions } from "../shared.js"; + +const backupFetch = maybe(() => fetch); + +export function createFetchMultipartSubscription( + uri: string, + { fetch: preferredFetch, headers }: CreateMultipartSubscriptionOptions = {} +) { + return function multipartSubscriptionForwarder({ + query, + variables, + }: { + query?: string; + variables: undefined | Record; + }) { + const body: Body = { variables, query }; + const options = generateOptionsForMultipartSubscription(headers || {}); + + return new Observable((observer) => { + try { + options.body = serializeFetchParameter(body, "Payload"); + } catch (parseError) { + observer.error(parseError); + } + + const currentFetch = preferredFetch || maybe(() => fetch) || backupFetch; + const observerNext = observer.next.bind(observer); + + currentFetch!(uri, options) + .then((response) => { + const ctype = response.headers?.get("content-type"); + + if (ctype !== null && /^multipart\/mixed/i.test(ctype)) { + return readMultipartBody(response, observerNext); + } + + observer.error(new Error("Expected multipart response")); + }) + .then(() => { + observer.complete(); + }) + .catch((err: any) => { + handleError(err, observer); + }); + }); + }; +} diff --git a/src/utilities/types/DeepOmit.ts b/src/utilities/types/DeepOmit.ts index 3e5b564ee09..9d1e31e6817 100644 --- a/src/utilities/types/DeepOmit.ts +++ b/src/utilities/types/DeepOmit.ts @@ -19,14 +19,12 @@ export type DeepOmitArray = { // This should be fine as of the time of this writing until omitDeep gets // broader use since this utility is only used to strip __typename from // `variables`; a case in which class instances are invalid anyways. -export type DeepOmit = T extends DeepOmitPrimitive - ? T +export type DeepOmit = + T extends DeepOmitPrimitive ? T : { - [P in Exclude]: T[P] extends infer TP - ? TP extends DeepOmitPrimitive - ? TP - : TP extends any[] - ? DeepOmitArray - : DeepOmit - : never; + [P in Exclude]: T[P] extends infer TP ? + TP extends DeepOmitPrimitive ? TP + : TP extends any[] ? DeepOmitArray + : DeepOmit + : never; }; diff --git a/src/utilities/types/DeepPartial.ts b/src/utilities/types/DeepPartial.ts index 6b9d501462d..891e0844936 100644 --- a/src/utilities/types/DeepPartial.ts +++ b/src/utilities/types/DeepPartial.ts @@ -18,23 +18,23 @@ import type { Primitive } from "./Primitive.js"; type DeepPartialPrimitive = Primitive | Date | RegExp; -export type DeepPartial = T extends DeepPartialPrimitive - ? T - : T extends Map - ? DeepPartialMap - : T extends ReadonlyMap - ? DeepPartialReadonlyMap - : T extends Set - ? DeepPartialSet - : T extends ReadonlySet - ? DeepPartialReadonlySet - : T extends (...args: any[]) => unknown - ? T | undefined - : T extends object - ? T extends ReadonlyArray // Test for arrays/tuples - ? TItem[] extends T // Test for non-tuples - ? readonly TItem[] extends T - ? ReadonlyArray> +export type DeepPartial = + T extends DeepPartialPrimitive ? T + : T extends Map ? DeepPartialMap + : T extends ReadonlyMap ? + DeepPartialReadonlyMap + : T extends Set ? DeepPartialSet + : T extends ReadonlySet ? DeepPartialReadonlySet + : T extends (...args: any[]) => unknown ? T | undefined + : T extends object ? + T extends ( + ReadonlyArray // Test for arrays/tuples + ) ? + TItem[] extends ( + T // Test for non-tuples + ) ? + readonly TItem[] extends T ? + ReadonlyArray> : Array> : DeepPartialObject : DeepPartialObject diff --git a/src/utilities/types/IsStrictlyAny.ts b/src/utilities/types/IsStrictlyAny.ts index 024b095e389..786c7225f81 100644 --- a/src/utilities/types/IsStrictlyAny.ts +++ b/src/utilities/types/IsStrictlyAny.ts @@ -1,8 +1,7 @@ // Returns true if T is any, or false for any other type. // Inspired by https://stackoverflow.com/a/61625296/128454. -export type IsStrictlyAny = UnionToIntersection> extends never - ? true - : false; +export type IsStrictlyAny = + UnionToIntersection> extends never ? true : false; // If (and only if) T is any, the union 'a' | 1 is returned here, representing // both branches of this conditional type. Only UnionForAny produces this @@ -13,8 +12,6 @@ type UnionForAny = T extends never ? "a" : 1; // should be 'a' & 1, which TypeScript simplifies to the never type, since the // literal type 'a' and the literal type 1 are incompatible. More explanation of // this helper type: https://stackoverflow.com/a/50375286/62076. -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void - ? I +type UnionToIntersection = + (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; diff --git a/src/utilities/types/OnlyRequiredProperties.ts b/src/utilities/types/OnlyRequiredProperties.ts new file mode 100644 index 00000000000..5264a0fca69 --- /dev/null +++ b/src/utilities/types/OnlyRequiredProperties.ts @@ -0,0 +1,6 @@ +/** + * Returns a new type that only contains the required properties from `T` + */ +export type OnlyRequiredProperties = { + [K in keyof T as {} extends Pick ? never : K]: T[K]; +}; diff --git a/src/utilities/types/TODO.ts b/src/utilities/types/TODO.ts new file mode 100644 index 00000000000..a5d637cc22c --- /dev/null +++ b/src/utilities/types/TODO.ts @@ -0,0 +1,2 @@ +/** @internal */ +export type TODO = any; diff --git a/tsconfig.json b/tsconfig.json index b03c9fb7fcb..cdb8b9cca26 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "skipLibCheck": true, "moduleResolution": "node", "importHelpers": true, - "removeComments": true, "sourceMap": true, "declaration": true, "declarationMap": true, @@ -16,8 +15,9 @@ "esModuleInterop": true, "experimentalDecorators": true, "outDir": "./dist", - "lib": ["es2015", "esnext.asynciterable", "dom"], - "jsx": "react" + "lib": ["es2015", "esnext.asynciterable"], + "jsx": "react", + "strict": true }, "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": ["src/**/__tests__/**/*"] diff --git a/tsconfig.tests.json b/tsconfig.tests.json index d6bb25bd3fd..0908742da0d 100644 --- a/tsconfig.tests.json +++ b/tsconfig.tests.json @@ -1,5 +1,3 @@ { - "extends": "./tsconfig.json", - "include": ["src/**/__tests__/**/*.ts", "src/**/__tests__/**/*.tsx"], - "exclude": [] + "extends": "./src/tsconfig.json" } diff --git a/tsdoc.json b/tsdoc.json new file mode 100644 index 00000000000..c49aafb4c6e --- /dev/null +++ b/tsdoc.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + + // Inherit the TSDoc configuration for API Extractor + "extends": ["@microsoft/api-extractor/extends/tsdoc-base.json"], + + "tagDefinitions": [ + { + "tagName": "@since", + "syntaxKind": "block", + "allowMultiple": false + }, + { + "tagName": "@docGroup", + "syntaxKind": "block", + "allowMultiple": false + } + ], + + "supportForTags": { + "@since": true, + "@docGroup": true + } +}