UNPKG

@apollo-orbit/angular

Version:

A full-featured GraphQL client for Angular

1 lines 105 kB
{"version":3,"file":"apollo-orbit.angular.mjs","sources":["../../src/cacheEx.ts","../../src/gql.ts","../../src/queryObservable.ts","../../src/signals/cacheQuery.ts","../../src/signals/fragment.ts","../../src/signals/mutation.ts","../../src/signals/query.ts","../../src/signals/subscription.ts","../../src/signals/apolloSignal.ts","../../src/apollo.ts","../../src/clientFactory.ts","../../src/internal/apolloRegistry.ts","../../src/internal/instanceFactory.ts","../../src/map.ts","../../src/tokens.ts","../../src/providers.ts","../../src/types.ts","../../src/apollo-orbit.angular.ts"],"sourcesContent":["import type { ApolloCache, DocumentNode, MissingFieldError, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport type { DeepPartial } from '@apollo/client/utilities';\nimport { Observable } from 'rxjs';\n\nexport interface CacheWatchQueryOptions<TData, TVariables> {\n query: DocumentNode | TypedDocumentNode<TData, TVariables>;\n variables?: TVariables;\n optimistic?: boolean;\n immediate?: boolean;\n /**\n * If set to true, the observable will emit the partial data that is available in the cache.\n * If set to false, the observable will throw an error if the complete data is not available in the cache.\n * @default false\n */\n returnPartialData?: boolean;\n}\n\nexport interface CacheWatchQueryCompleteResult<TData> {\n data: TData;\n complete: true;\n missing?: never;\n}\n\nexport interface CacheWatchQueryPartialResult<TData> {\n data: DeepPartial<TData> | undefined;\n complete: false;\n missing?: Array<MissingFieldError>;\n}\n\nexport type CacheWatchQueryResult<TData> =\n | CacheWatchQueryCompleteResult<TData>\n | CacheWatchQueryPartialResult<TData>;\n\nexport interface ApolloCacheEx extends ApolloCache {\n /**\n * Watches the cache store for the query document provided.\n */\n watchQuery<TData = unknown, TVariables extends Variables = Variables>(options: CacheWatchQueryOptions<TData, TVariables> & { returnPartialData: true }): Observable<CacheWatchQueryPartialResult<TData>>;\n watchQuery<TData = unknown, TVariables extends Variables = Variables>(options: CacheWatchQueryOptions<TData, TVariables>): Observable<CacheWatchQueryCompleteResult<TData>>;\n}\n\nexport function extendCache(cache: ApolloCache): ApolloCacheEx {\n return Object.defineProperties(cache, {\n watchQuery: {\n value: watchQuery,\n writable: false,\n configurable: false\n }\n }) as ApolloCacheEx;\n}\n\nfunction watchQuery<TData, TVariables extends Variables = Variables>(\n this: ApolloCache,\n options: CacheWatchQueryOptions<TData, TVariables>\n): Observable<CacheWatchQueryResult<TData>> {\n const { immediate = true, optimistic = true } = options;\n return new Observable<CacheWatchQueryResult<TData>>(\n subscriber => {\n try {\n return this.watch<TData, TVariables>({\n ...options,\n optimistic,\n immediate,\n callback: ({ result, ...rest }) => {\n subscriber.next({ ...rest, data: result } as CacheWatchQueryResult<TData>);\n }\n });\n } catch (error) {\n subscriber.error(error);\n return void 0;\n }\n });\n}\n","import { DocumentNode, TypedDocumentNode } from '@apollo/client';\nimport { FragmentDefinitionNode } from 'graphql';\n\nexport interface FragmentIdentifier<TData> {\n id: string;\n fragmentName: string;\n fragment: DocumentNode | TypedDocumentNode<TData>;\n}\n\nexport function identifyFragment<TData = unknown>(\n fragment: DocumentNode | TypedDocumentNode<TData>,\n id: string,\n fragmentName?: string\n): FragmentIdentifier<TData> {\n const fragmentDefinition = findFragmentDefinition(fragment, fragmentName);\n\n return {\n id: `${fragmentDefinition.typeCondition.name.value}:${id}`,\n fragmentName: fragmentName ?? fragmentDefinition.name.value,\n fragment\n };\n}\n\nexport function identifyFragmentType<TData = unknown>(fragment: DocumentNode | TypedDocumentNode<TData>, fragmentName?: string): string {\n return findFragmentDefinition(fragment, fragmentName).typeCondition.name.value;\n}\n\nfunction findFragmentDefinition(fragment: DocumentNode, fragmentName?: string): FragmentDefinitionNode {\n const fragmentDefinition = fragment.definitions.find<FragmentDefinitionNode>((definition): definition is FragmentDefinitionNode =>\n definition.kind === 'FragmentDefinition' &&\n (fragmentName === undefined || definition.name.value === fragmentName));\n\n if (fragmentDefinition === undefined) throw new Error('Fragment definition was not found.');\n\n return fragmentDefinition;\n}\n","import { DataState, ObservableQuery, OperationVariables, TypedDocumentNode, UpdateQueryMapFn, OperationVariables as Variables } from '@apollo/client';\nimport { Observable, Subscription } from 'rxjs';\nimport { ExtraWatchQueryOptions, GetData, QueryResult, SingleQueryResult, SubscribeToMoreOptions, WatchQueryOptions } from './types';\n\nexport class QueryObservable<\n TData = unknown,\n TVariables extends Variables = Variables,\n TStates extends DataState<TData>['dataState'] = DataState<TData>['dataState']\n> extends Observable<QueryResult<TData, TStates>> {\n private previousData: GetData<TData, TStates> | undefined;\n\n public constructor(\n private readonly observableQuery: ObservableQuery<TData, TVariables>,\n { notifyOnLoading = true }: ExtraWatchQueryOptions\n ) {\n super(subscriber => {\n let subscription: Subscription | undefined;\n\n const next = ({ partial, ...result }: ObservableQuery.Result<TData>): void => {\n const { previousData } = this;\n this.previousData = (result.data ?? previousData) as GetData<TData, TStates> | undefined;\n subscriber.next({ ...result, previousData } as QueryResult<TData, TStates>);\n };\n\n const complete = (): void => {\n subscription = undefined;\n subscriber.complete();\n };\n\n subscription = observableQuery.subscribe({\n next: notifyOnLoading ? next : skipInitialLoading(next),\n complete\n });\n\n return () => {\n subscription?.unsubscribe();\n subscription = undefined;\n };\n });\n }\n\n public get query(): TypedDocumentNode<TData, TVariables> {\n return this.observableQuery.query;\n }\n\n public get variables(): TVariables | undefined {\n return this.observableQuery.variables;\n }\n\n public get options(): ObservableQuery.Options<TData, TVariables> {\n return this.observableQuery.options;\n }\n\n public get queryName(): string | undefined {\n return this.observableQuery.queryName;\n }\n\n public getCurrentResult(): QueryResult<TData, TStates> {\n const { partial, ...result } = this.observableQuery.getCurrentResult();\n return result as QueryResult<TData, TStates>;\n }\n\n /**\n * Update the variables of this observable query, and fetch the new results.\n * This method should be preferred over `setVariables` in most use cases.\n *\n * Returns a `ResultPromise` with an additional `.retain()` method. Calling\n * `.retain()` keeps the network operation running even if the `ObservableQuery`\n * no longer requires the result.\n *\n * Note: `refetch()` guarantees that a value will be emitted from the\n * observable, even if the result is deep equal to the previous value.\n *\n * @param variables - The new set of variables. If there are missing variables,\n * the previous values of those variables will be used.\n */\n public refetch(variables?: Partial<TVariables>): Promise<SingleQueryResult<TData>> {\n return this.observableQuery.refetch(variables);\n }\n\n public fetchMore<\n TFetchData = TData,\n TFetchVars extends OperationVariables = TVariables\n >(options: ObservableQuery.FetchMoreOptions<TData, TVariables, TFetchData, TFetchVars>): Promise<SingleQueryResult<TFetchData>> {\n return this.observableQuery.fetchMore(options);\n }\n\n public subscribeToMore<\n TSubscriptionData = TData,\n TSubscriptionVariables extends Variables = TVariables\n >(\n options: SubscribeToMoreOptions<\n TData,\n TSubscriptionVariables,\n TSubscriptionData,\n TVariables\n >\n ): () => void {\n const { subscription: document, ...rest } = options;\n return this.observableQuery.subscribeToMore<TSubscriptionData, TSubscriptionVariables>({ document, ...rest });\n }\n\n /**\n * Update the variables of this observable query, and fetch the new results\n * if they've changed. Most users should prefer `refetch` instead of\n * `setVariables` in order to to be properly notified of results even when\n * they come from the cache.\n *\n * Note: `setVariables()` guarantees that a value will be emitted from the\n * observable, even if the result is deeply equal to the previous value.\n *\n * Note: the promise will resolve with the last emitted result\n * when either the variables match the current variables or there\n * are no subscribers to the query.\n *\n * @param variables - The new set of variables. If there are missing variables,\n * the previous values of those variables will be used.\n */\n public setVariables(variables: TVariables): Promise<SingleQueryResult<TData>> {\n return this.observableQuery.setVariables(variables);\n }\n\n /**\n * A function that enables you to update the query's cached result without executing a followup GraphQL operation.\n *\n * See [using updateQuery and updateFragment](https://www.apollographql.com/docs/react/caching/cache-interaction/#using-updatequery-and-updatefragment) for additional information.\n */\n public updateQuery(mapFn: UpdateQueryMapFn<TData, TVariables>): void {\n return this.observableQuery.updateQuery(mapFn);\n }\n\n /**\n * A function that instructs the query to begin re-executing at a specified interval (in milliseconds).\n */\n public startPolling(pollInterval: number): void {\n this.observableQuery.startPolling(pollInterval);\n }\n\n /**\n * A function that instructs the query to stop polling after a previous call to `startPolling`.\n */\n public stopPolling(): void {\n return this.observableQuery.stopPolling();\n }\n\n /**\n * Reevaluate the query, optionally against new options. New options will be\n * merged with the current options when given.\n *\n * Note: `variables` can be reset back to their defaults (typically empty) by calling `reobserve` with\n * `variables: undefined`.\n */\n public reobserve(newOptions?: Partial<WatchQueryOptions<TData, TVariables>>): Promise<SingleQueryResult<TData>> {\n return this.observableQuery.reobserve(newOptions);\n }\n\n public hasObservers(): boolean {\n return this.observableQuery.hasObservers();\n }\n\n /**\n * Tears down the `ObservableQuery` and stops all active operations by sending a `complete` notification.\n */\n public stop(): void {\n this.observableQuery.stop();\n }\n}\n\nfunction skipInitialLoading<TFunc extends (result: ObservableQuery.Result<any>) => void>(fn: TFunc): TFunc {\n let first = true;\n return ((result: ObservableQuery.Result<any>) => {\n const skipped = first && result.loading;\n first = false;\n if (skipped) return;\n return fn(result);\n }) as TFunc;\n}\n","import { computed, effect, Injector, Signal, signal, WritableSignal } from '@angular/core';\nimport { DocumentNode, MissingFieldError, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport { equal } from '@wry/equality';\nimport { Subscription } from 'rxjs';\nimport { ApolloCacheEx, CacheWatchQueryCompleteResult, CacheWatchQueryPartialResult } from '../cacheEx';\n\nexport interface SignalCacheQueryOptions<\n TData = unknown,\n TVariables extends Variables = Variables\n> {\n /**\n * A GraphQL query document parsed into an AST by gql.\n */\n query: DocumentNode | TypedDocumentNode<TData, TVariables>;\n\n /**\n * An object containing all of the variables your query needs to execute.\n * Can be provided as a static object, a signal, or a function that returns the variables.\n * When provided as a function, it will be executed in a computed context and will\n * automatically re-execute the query when any reactive dependencies change.\n */\n variables?: NoInfer<TVariables> | (() => NoInfer<TVariables>);\n\n /**\n * If `true`, the query will be evaluated against both the optimistic cache layer\n * and the normal cache layer. This allows optimistic updates to be reflected\n * in the query results immediately.\n * @default true\n */\n optimistic?: boolean;\n\n immediate?: boolean;\n\n /**\n * If set to `true`, the observable will emit the partial data that is available in the cache.\n * If set to `false`, the observable will throw an error if the complete data is not available in the cache.\n * @default false\n */\n returnPartialData?: boolean;\n\n /**\n * Custom injector to use for this signal.\n */\n injector?: Injector;\n}\n\ntype SignalCacheQueryResult<TData> = TData extends undefined\n ? CacheWatchQueryPartialResult<TData>\n : CacheWatchQueryCompleteResult<TData>;\n\nexport class SignalCacheQuery<TData, TVariables extends Variables = Variables> {\n /**\n * The cache query result, containing `data`, `complete`, and `missing`.\n */\n public readonly result: Signal<SignalCacheQueryResult<TData>>;\n\n /**\n * The data returned by the cache query.\n */\n public readonly data: Signal<TData>;\n\n /**\n * A signal indicating whether the query result contains complete data.\n * - `true`: All requested fields are available in the cache\n * - `false`: Some fields are missing from the cache\n * - `undefined`: Query has not been executed yet\n */\n public readonly complete: Signal<boolean | undefined>;\n\n /**\n * A signal containing an array of missing field errors if the query is incomplete.\n * Will be `undefined` if the query is complete or has not been executed.\n */\n public readonly missing: Signal<Array<MissingFieldError> | undefined>;\n\n private readonly _result: WritableSignal<SignalCacheQueryResult<TData>>;\n private readonly variables: Signal<TVariables | undefined>;\n private subscription: Subscription | undefined;\n\n public constructor(\n injector: Injector,\n private readonly cache: ApolloCacheEx,\n private readonly options: SignalCacheQueryOptions<TData, TVariables>\n ) {\n this._result = signal<SignalCacheQueryResult<TData>>({\n data: undefined,\n complete: false\n } as SignalCacheQueryResult<TData>);\n\n const { variables } = options;\n this.result = this._result.asReadonly();\n this.data = computed(() => this.result().data as TData);\n this.complete = computed(() => this.result().complete);\n this.missing = computed(() => this.result().missing);\n this.variables = typeof variables === 'function' ? computed(variables, { equal }) : signal(variables);\n\n effect(onCleanup => {\n this.subscription = this.subscribe(this.variables());\n\n onCleanup(() => {\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n });\n }, { injector });\n }\n\n private subscribe(variables: TVariables | undefined): Subscription {\n return this.cache\n .watchQuery({ ...this.options, variables })\n .subscribe(result => this._result.set(result as SignalCacheQueryResult<TData>));\n }\n}\n","import { computed, effect, Injector, Signal, signal, WritableSignal } from '@angular/core';\nimport { DocumentNode, FragmentType, Reference, StoreObject, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport type { MissingTree } from '@apollo/client/cache';\nimport type { DeepPartial } from '@apollo/client/utilities';\nimport { equal } from '@wry/equality';\nimport { Subscription } from 'rxjs';\nimport { Apollo } from '../apollo';\n\nexport type SignalFragmentResult<TData> =\n | { data: TData; complete: true; missing?: never }\n | { data: DeepPartial<TData>; complete: false; missing?: MissingTree };\n\n// import { ApolloCache.WatchFragmentOptions as SignalFragmentOptions } from '@apollo/client';\nexport interface SignalFragmentOptions<TData = unknown, TVariables extends Variables = Variables> {\n /**\n * A GraphQL fragment document parsed into an AST with the `gql`\n * template literal.\n *\n * @docGroup 1. Required options\n */\n fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;\n /**\n * An object containing a `__typename` and primary key fields\n * (such as `id`) identifying the entity object from which the fragment will\n * be retrieved, or a `{ __ref: \"...\" }` reference, or a `string` ID\n * (uncommon).\n *\n * @docGroup 1. Required options\n */\n from:\n | StoreObject | Reference | FragmentType<NoInfer<TData>> | string\n | (() => StoreObject | Reference | FragmentType<NoInfer<TData>> | string);\n /**\n * Any variables that the GraphQL fragment may depend on.\n *\n * @docGroup 2. Cache options\n */\n variables?: NoInfer<TVariables> | (() => NoInfer<TVariables>);\n /**\n * The name of the fragment defined in the fragment document.\n *\n * Required if the fragment document includes more than one fragment,\n * optional otherwise.\n *\n * @docGroup 2. Cache options\n */\n fragmentName?: string;\n /**\n * If `true`, `watchFragment` returns optimistic results.\n *\n * The default value is `true`.\n *\n * @docGroup 2. Cache options\n */\n optimistic?: boolean;\n\n /**\n * Custom injector to use for this signal.\n */\n injector?: Injector;\n}\n\nexport class SignalFragment<TData, TVariables extends Variables = Variables> {\n /**\n * The fragment result, containing `data`, `complete`, and `missing`.\n */\n public readonly result: Signal<SignalFragmentResult<TData>>;\n\n /**\n * The data returned by the fragment.\n */\n public readonly data: Signal<DeepPartial<TData>>;\n\n /**\n * `true` if all requested fields in the fragment are present in the cache, `false` otherwise.\n */\n public readonly complete: Signal<boolean>;\n\n /**\n * If `complete` is `false`, this field describes which fields are missing.\n */\n public readonly missing: Signal<MissingTree | undefined>;\n\n private readonly _result: WritableSignal<SignalFragmentResult<TData>>;\n private readonly from: Signal<StoreObject | Reference | string>;\n private readonly variables: Signal<TVariables | undefined>;\n private subscription: Subscription | undefined;\n\n public constructor(\n injector: Injector,\n private readonly apollo: Apollo,\n private readonly options: SignalFragmentOptions<TData, TVariables>\n ) {\n this._result = signal({\n data: {} as DeepPartial<TData>,\n complete: false\n });\n\n const { variables, from } = options;\n\n this.result = this._result.asReadonly();\n this.data = computed(() => this.result().data as DeepPartial<TData>);\n this.complete = computed(() => this.result().complete);\n this.missing = computed(() => this.result().missing);\n this.from = typeof from === 'function' ? computed(from, { equal }) : signal(from);\n this.variables = typeof variables === 'function' ? computed(variables, { equal }) : signal(variables);\n\n effect(onCleanup => {\n const from = this.from();\n const variables = this.variables();\n this.subscription = this.subscribe(from, variables);\n\n onCleanup(() => {\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n });\n }, { injector });\n }\n\n private subscribe(from: StoreObject | Reference | string, variables: TVariables | undefined): Subscription {\n return this.apollo.watchFragment({\n ...this.options,\n from,\n variables\n }).subscribe(result => this._result.set(result));\n }\n}\n","import { computed, Signal, signal, untracked, WritableSignal } from '@angular/core';\nimport { ApolloClient, DocumentNode, ErrorLike, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport { mergeOptions } from '@apollo/client/utilities/internal';\nimport { firstValueFrom } from 'rxjs';\nimport { Apollo } from '../apollo';\nimport { MutationOptions, MutationResult } from '../types';\n\nexport interface SignalMutationResult<TData> extends MutationResult<TData> {\n loading: boolean;\n called: boolean;\n}\n\nexport type SignalMutationOptions<TData = unknown, TVariables extends Variables = Variables> = Omit<SignalMutationExecutionOptions<TData, TVariables>, 'variables'>;\n\nexport type SignalMutationExecutionOptions<TData = unknown, TVariables extends Variables = Variables> =\n & Omit<MutationOptions<TData, TVariables>, 'mutation'>\n & {\n /**\n * Callback executed when the mutation completes successfully.\n */\n onData?: (data: TData, options?: MutationOptions<TData, TVariables>) => void;\n\n /**\n * Callback executed when the mutation encounters an error.\n */\n onError?: (error: ErrorLike, options?: MutationOptions<TData, TVariables>) => void;\n };\n\nexport class SignalMutation<TData, TVariables extends Variables = Variables> {\n /**\n * The mutation result, containing `data`, `loading`, and `error` and `called`.\n */\n public readonly result: Signal<SignalMutationResult<TData>>;\n\n /**\n * If `true`, the mutation is currently in flight.\n */\n public readonly loading: Signal<boolean>;\n\n /**\n * The data returned from the mutation.\n */\n public readonly data: Signal<TData | undefined>;\n\n /**\n * The error encountered during the mutation.\n */\n public readonly error: Signal<ErrorLike | undefined>;\n\n /**\n * If `true`, the mutation's mutate method has been called.\n */\n public readonly called: Signal<boolean>;\n\n private readonly _result: WritableSignal<SignalMutationResult<TData>>;\n private mutationId: number;\n\n public constructor(\n private readonly apollo: Apollo,\n private readonly mutation: DocumentNode | TypedDocumentNode<TData, TVariables>,\n private readonly options?: SignalMutationOptions<TData, TVariables>\n ) {\n this.mutationId = 0;\n\n this._result = signal({ data: undefined, called: false, loading: false });\n this.result = this._result.asReadonly();\n this.loading = computed(() => this.result().loading);\n this.data = computed(() => this.result().data);\n this.error = computed(() => this.result().error);\n this.called = computed(() => this.result().called);\n }\n\n /**\n * Execute the mutation with the provided variables and options.\n */\n public mutate(...[executeOptions]: {} extends TVariables // eslint-disable-line @typescript-eslint/no-empty-object-type\n ? [executeOptions?: SignalMutationExecutionOptions<TData, TVariables>]\n : [executeOptions: SignalMutationExecutionOptions<TData, TVariables>]\n ): Promise<MutationResult<TData>> {\n if (!untracked(this.loading)) {\n this._result.set({\n loading: true,\n error: undefined,\n data: undefined,\n called: true\n });\n }\n\n // Increment the mutation ID to track concurrent mutations\n const mutationId = ++this.mutationId;\n\n const { mutation } = this;\n const { onData, onError, ...mergedOptions } = mergeOptions(this.options ?? {}, executeOptions ?? {});\n const mutationOptions = { ...mergedOptions, mutation } as ApolloClient.MutateOptions<TData, TVariables>;\n\n return firstValueFrom(this.apollo.mutate<TData, TVariables>(mutationOptions))\n .then(result => {\n const { data, error } = result;\n\n if (error) {\n onError?.(error, mutationOptions);\n }\n\n if (!error && data !== undefined) {\n onData?.(data, mutationOptions);\n }\n\n // Only update signal if this is still the current mutation\n if (mutationId === this.mutationId) {\n const newResult = {\n called: true,\n loading: false,\n data,\n error\n };\n\n this._result.set(newResult);\n }\n\n return result;\n })\n .catch(error => {\n onError?.(error, mutationOptions);\n\n // Only update signal if this is still the current mutation\n if (mutationId === this.mutationId) {\n const newResult = {\n called: true,\n loading: false,\n data: undefined,\n error\n };\n\n this._result.set(newResult);\n }\n\n return { data: undefined, error };\n });\n }\n\n /**\n * Reset the mutation result to its initial state.\n */\n public reset(): void {\n this.mutationId = 0;\n this._result.set({\n data: undefined,\n called: false,\n loading: false\n });\n }\n}\n","import { computed, DestroyRef, effect, Injector, linkedSignal, PendingTasks, signal, Signal, untracked, WritableSignal } from '@angular/core';\nimport { ApolloClient, DataState, DefaultContext, DocumentNode, ErrorLike, ErrorPolicy, NetworkStatus, ObservableQuery, RefetchWritePolicy, TypedDocumentNode, UpdateQueryMapFn, OperationVariables as Variables, WatchQueryFetchPolicy } from '@apollo/client';\nimport { equal } from '@wry/equality';\nimport { noop, Subscription } from 'rxjs';\nimport { Apollo } from '../apollo';\nimport { QueryObservable } from '../queryObservable';\nimport type { GetData, QueryResult, SingleQueryResult, SubscribeToMoreOptions, WatchQueryOptions } from '../types';\nimport type { SignalVariablesOption } from './types';\n\nexport class SignalQueryExecutionError extends Error {\n public constructor(methodName: keyof SignalQuery<any, any>) {\n super(`'${methodName}' cannot be called while the query is not active.`);\n this.name = 'SignalQueryExecutionError';\n }\n}\n\n// import { ApolloClient.WatchQueryOptions as SignalQueryOptions } from '@apollo/client';\nexport type SignalQueryOptions<TData = unknown, TVariables extends Variables = Variables> = {\n /**\n * 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).\n *\n * For details, see [Setting a fetch policy](https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy).\n *\n * The default value is `cache-first`.\n *\n * @docGroup 3. Caching options\n */\n fetchPolicy?: WatchQueryFetchPolicy;\n /**\n * Specifies the `FetchPolicy` to be used after this query has completed.\n *\n * @docGroup 3. Caching options\n */\n nextFetchPolicy?: ApolloClient.WatchQueryOptions<TData, TVariables>['nextFetchPolicy'];\n /**\n * Defaults to the initial value of options.fetchPolicy, but can be explicitly\n * configured to specify the WatchQueryFetchPolicy to revert back to whenever\n * variables change (unless nextFetchPolicy intervenes).\n *\n * @docGroup 3. Caching options\n */\n initialFetchPolicy?: WatchQueryFetchPolicy;\n /**\n * Specifies whether a `NetworkStatus.refetch` operation should merge\n * incoming field data with existing data, or overwrite the existing data.\n * Overwriting is probably preferable, but merging is currently the default\n * behavior, for backwards compatibility with Apollo Client 3.x.\n *\n * @docGroup 3. Caching options\n */\n refetchWritePolicy?: RefetchWritePolicy;\n /**\n * Specifies how the query handles a response that returns both GraphQL errors and partial results.\n *\n * For details, see [GraphQL error policies](https://www.apollographql.com/docs/react/data/error-handling/#graphql-error-policies).\n *\n * The default value is `none`, meaning that the query result includes error details but not partial results.\n *\n * @docGroup 1. Operation options\n */\n errorPolicy?: ErrorPolicy;\n /**\n * 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.\n *\n * @docGroup 2. Networking options\n */\n context?: DefaultContext;\n /**\n * Specifies the interval (in milliseconds) at which the query polls for updated results.\n *\n * The default value is `0` (no polling).\n *\n * @docGroup 2. Networking options\n */\n pollInterval?: number;\n /**\n * If `true`, the in-progress query's associated component re-renders whenever the network status changes or a network error occurs.\n *\n * The default value is `true`.\n *\n * @docGroup 2. Networking options\n */\n notifyOnNetworkStatusChange?: boolean;\n /**\n * If `true`, the query can return partial results from the cache if the cache doesn't contain results for all queried fields.\n *\n * The default value is `false`.\n *\n * @docGroup 3. Caching options\n */\n returnPartialData?: boolean;\n /**\n * A callback function that's called whenever a refetch attempt occurs\n * while polling. If the function returns `true`, the refetch is\n * skipped and not reattempted until the next poll interval.\n *\n * @docGroup 2. Networking options\n */\n skipPollAttempt?: () => boolean;\n /**\n * A GraphQL query string parsed into an AST with the gql template literal.\n *\n * @docGroup 1. Operation options\n */\n query: DocumentNode | TypedDocumentNode<TData, TVariables>;\n\n /**\n * Whether or not to track initial network loading status.\n * @default true\n */\n notifyOnLoading?: boolean;\n\n /**\n * Whether to execute query immediately or lazily via `execute` method.\n */\n lazy?: boolean;\n\n /**\n * Custom injector to use for this query.\n */\n injector?: Injector;\n} & (\n | {\n /**\n * Whether to execute query immediately or lazily via `execute` method.\n */\n lazy: true;\n\n /**\n * A function or signal returning an object containing all of the GraphQL variables your query requires to execute.\n *\n * Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value.\n *\n * When `null` is returned, the query will be terminated until a non-null value is returned again.\n */\n variables?: () => TVariables | undefined | null;\n }\n | SignalVariablesOption<NoInfer<TVariables>>\n );\n\nexport interface SignalQueryExecOptions<TVariables extends Variables = Variables> {\n /**\n * Variables to use for this query execution.\n */\n variables?: TVariables;\n\n /**\n * Context to use for this execution.\n */\n context?: DefaultContext;\n}\n\nexport class SignalQuery<TData, TVariables extends Variables = Variables, TStates extends DataState<TData>['dataState'] = 'empty' | 'complete' | 'streaming'> {\n /**\n * The query result, containing `data`, `loading`, `error`, `networkStatus`, `previousData`, `dataState`.\n */\n public readonly result: Signal<QueryResult<TData, TStates>>;\n\n /**\n * If `true`, the query is currently in flight.\n */\n public readonly loading: Signal<boolean> = computed(() => this.result().loading);\n\n /**\n * The current network status of the query.\n */\n public readonly networkStatus: Signal<NetworkStatus> = computed(() => this.result().networkStatus);\n\n /**\n * The data returned by the query, or `undefined` if loading, errored, or no data received yet.\n */\n public readonly data = computed<GetData<TData, TStates> | undefined>(() => this.result().data as GetData<TData, TStates> | undefined);\n\n /**\n * The data from the previous successful result, useful for displaying stale data during refetches.\n */\n public readonly previousData = computed<GetData<TData, TStates> | undefined>(() => this.result().previousData);\n\n /**\n * An error object if the query failed, `undefined` otherwise.\n */\n public readonly error: Signal<ErrorLike | undefined> = computed(() => this.result().error);\n\n /**\n * A writable signal that represents the current query variables.\n */\n public readonly variables: WritableSignal<TVariables | undefined | null>;\n\n /**\n * Whether the query is currently active, subscribed to the underlying observable and receiving cache updates.\n */\n public readonly active: Signal<boolean>;\n\n /**\n * Whether the query is currently enabled.\n *\n * This property starts as `true` for non-lazy queries and `false` for lazy queries.\n *\n * Calling `execute()` sets it to `true`, while calling `terminate()` sets it to `false`.\n *\n * When `true`:\n * - The query automatically executes when variables change from `null` to a non-null value\n * - Variable changes trigger re-execution with the new variables\n *\n * When `false`:\n * - Variable changes are ignored and do not trigger re-execution\n * - The query must be manually started via `execute()`\n *\n * Note: This is different from `active`, which indicates whether the query is currently connected to its observable and actively watching the cache.\n */\n public readonly enabled: Signal<boolean>;\n\n private observable: QueryObservable<TData, TVariables, TStates> | undefined;\n private subscription: Subscription | undefined;\n private readonly _active: WritableSignal<boolean>;\n private readonly _result: WritableSignal<QueryResult<TData, TStates>>;\n private readonly _enabled: WritableSignal<boolean>;\n\n public constructor(\n injector: Injector,\n private readonly apollo: Apollo,\n private readonly options: SignalQueryOptions<TData, TVariables>\n ) {\n const { variables, lazy = false } = options;\n\n this.variables = variables !== undefined ? linkedSignal(variables, { equal }) : signal(variables);\n\n this._enabled = signal(!lazy);\n this.enabled = this._enabled.asReadonly();\n\n this._result = signal({ data: undefined, dataState: 'empty', loading: false, networkStatus: NetworkStatus.ready } as QueryResult<TData, TStates>);\n this.result = this._result.asReadonly();\n\n this._active = signal(false);\n this.active = this._active.asReadonly();\n\n effect(() => {\n const variables = this.variables();\n const enabled = untracked(this.enabled);\n const active = untracked(this.active);\n\n if (!enabled) return;\n\n if (variables !== null) {\n if (!active) {\n this._execute({ variables }).catch(noop);\n } else {\n this.observable?.setVariables(variables as TVariables).catch(noop);\n }\n } else if (active) {\n this._terminate();\n }\n }, { injector });\n\n if (!lazy) {\n const pendingTasks = injector.get(PendingTasks);\n\n effect(() => {\n if (untracked(this.variables) !== null) {\n const resolvePendingTask = pendingTasks.add();\n this.execute().then(resolvePendingTask).catch(noop);\n }\n }, { injector });\n }\n\n injector.get(DestroyRef).onDestroy(() => this.terminate());\n }\n\n /**\n * Execute the query with the provided options.\n */\n public execute(execOptions: SignalQueryExecOptions<TVariables> = {}): Promise<SingleQueryResult<TData>> {\n this._enabled.set(true);\n return this._execute(execOptions);\n }\n\n /**\n * Terminate query execution and unsubscribe from the observable.\n */\n public terminate(): void {\n this._enabled.set(false);\n this._terminate();\n }\n\n /**\n * Refetch the query with the current variables.\n */\n public refetch(variables?: Partial<TVariables>): Promise<SingleQueryResult<TData>> {\n if (!this.observable) throw new SignalQueryExecutionError('refetch');\n return this.observable.refetch(variables)\n .catch(error => ({ data: undefined, error }));\n }\n\n /**\n * Fetch more data and merge it with the existing result.\n */\n public fetchMore<\n TFetchData = TData,\n TFetchVars extends Variables = TVariables\n >(options: ObservableQuery.FetchMoreOptions<TData, TVariables, TFetchData, TFetchVars>): Promise<SingleQueryResult<TFetchData>> {\n if (!this.observable) throw new SignalQueryExecutionError('fetchMore');\n return this.observable.fetchMore(options)\n .catch(error => ({ data: undefined, error }));\n }\n\n /**\n * Update the query's cached data.\n */\n public updateQuery(mapFn: UpdateQueryMapFn<TData, TVariables>): void {\n if (!this.observable) throw new SignalQueryExecutionError('updateQuery');\n this.observable.updateQuery(mapFn);\n }\n\n /**\n * Start polling the query.\n */\n public startPolling(pollInterval: number): void {\n if (!this.observable) throw new SignalQueryExecutionError('startPolling');\n this.observable.startPolling(pollInterval);\n }\n\n /**\n * Stop polling the query.\n */\n public stopPolling(): void {\n if (!this.observable) throw new SignalQueryExecutionError('stopPolling');\n this.observable.stopPolling();\n }\n\n /**\n * Subscribe to more data.\n */\n public subscribeToMore<\n TSubscriptionData = TData,\n TSubscriptionVariables extends Variables = TVariables\n >(\n options: SubscribeToMoreOptions<\n TData,\n TSubscriptionVariables,\n TSubscriptionData,\n TVariables\n >\n ): () => void {\n if (!this.observable) throw new SignalQueryExecutionError('subscribeToMore');\n return this.observable.subscribeToMore<TSubscriptionData, TSubscriptionVariables>(options);\n }\n\n private _execute(execOptions: SignalQueryExecOptions<TVariables> = {}): Promise<SingleQueryResult<TData>> {\n if ('variables' in execOptions) {\n this.variables.set(execOptions.variables);\n }\n\n const variables = untracked(this.variables);\n\n if (variables === null) {\n return Promise.resolve({ data: this.data() as TData | undefined });\n }\n\n this._active.set(true);\n\n const { query, lazy, notifyOnLoading = true, notifyOnNetworkStatusChange = true, ...options } = this.options;\n\n const newOptions = {\n ...options,\n ...execOptions,\n notifyOnLoading,\n notifyOnNetworkStatusChange,\n query,\n variables\n } as WatchQueryOptions<TData, TVariables>;\n\n this.observable ??= this.apollo.watchQuery<TData, TVariables>(newOptions) as QueryObservable<TData, TVariables, any>;\n this.subscription ??= this.observable.subscribe(result => this._result.set(result));\n\n return this.observable.reobserve(newOptions)\n .catch(error => ({ data: undefined, error }));\n }\n\n private _terminate(): void {\n this._active.set(false);\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n this.observable = undefined;\n this._result.update(({ data, previousData }) => ({\n data: undefined,\n dataState: 'empty',\n loading: false,\n networkStatus: NetworkStatus.ready,\n previousData: data ?? previousData\n }) as QueryResult<TData, TStates>);\n }\n}\n","import { computed, DestroyRef, effect, Injector, linkedSignal, Signal, signal, untracked, WritableSignal } from '@angular/core';\nimport { DefaultContext, DocumentNode, ErrorLike, ErrorPolicy, FetchPolicy, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport { equal } from '@wry/equality';\nimport { Subscription } from 'rxjs';\nimport { Apollo } from '../apollo';\nimport { SubscriptionOptions, SubscriptionResult } from '../types';\nimport { SignalVariablesOption } from './types';\n\n// import { ApolloClient.SubscribeOptions as SignalSubscriptionOptions } from '@apollo/client';\nexport type SignalSubscriptionOptions<TData = unknown, TVariables extends Variables = Variables> = {\n /**\n * A GraphQL document, often created with `gql` from the `graphql-tag`\n * package, that contains a single subscription inside of it.\n */\n subscription: DocumentNode | TypedDocumentNode<TData, TVariables>;\n /**\n * 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).\n */\n fetchPolicy?: FetchPolicy;\n /**\n * Specifies the `ErrorPolicy` to be used for this operation\n */\n errorPolicy?: ErrorPolicy;\n /**\n * Shared context between your component and your network interface (Apollo Link).\n */\n context?: DefaultContext;\n /**\n * Shared context between your component and your network interface (Apollo Link).\n */\n extensions?: Record<string, any>;\n\n /**\n * Whether to execute subscription immediately or lazily via `execute` method.\n */\n lazy?: boolean;\n\n /**\n * Callback for when new data is received\n */\n onData?: (data: TData) => void;\n\n /**\n * Callback for when the subscription is completed\n */\n onComplete?: () => void;\n\n /**\n * Callback for when an error occurs\n */\n onError?: (error: ErrorLike) => void;\n\n /**\n * Custom injector to use for this subscription.\n */\n injector?: Injector;\n} & (\n | {\n /**\n * Whether to execute subscription immediately or lazily via `execute` method.\n */\n lazy: true;\n\n /**\n * A function or signal returning an object containing all of the GraphQL variables your operation requires to execute.\n *\n * Each key in the object corresponds to a variable name, and that key's value corresponds to the variable value.*\n *\n * When `null` is returned, the subscription will be terminated until a non-null value is returned again.\n */\n variables?: () => TVariables | undefined | null;\n }\n | SignalVariablesOption<NoInfer<TVariables>>\n );\n\nexport interface SignalSubscriptionExecOptions<TVariables extends Variables = Variables> {\n /**\n * Variables to use for this query execution.\n */\n variables?: TVariables;\n\n /**\n * Context to use for this execution.\n */\n context?: DefaultContext;\n}\n\nexport interface SignalSubscriptionResult<TData> extends SubscriptionResult<TData> {\n /**\n * Whether the subscription is currently loading\n */\n loading: boolean;\n}\n\nexport class SignalSubscription<TData, TVariables extends Variables = Variables> {\n /**\n * The subscription result, containing `data`, `loading`, and `error`.\n */\n public readonly result: Signal<SignalSubscriptionResult<TData>>;\n\n /**\n * If `true`, the subscription is currently loading the initial result.\n */\n public readonly loading: Signal<boolean>;\n\n /**\n * The data returned by the subscription, or `undefined` if loading, errored, or no data received yet.\n */\n public readonly data: Signal<TData | undefined>;\n\n /**\n * An error object if the subscription failed, `undefined` otherwise.\n */\n public readonly error: Signal<ErrorLike | undefined>;\n\n /**\n * A writable signal that represents the current subscription variables.\n */\n public readonly variables: WritableSignal<TVariables | undefined | null>;\n\n /**\n * Whether the subscription is currently active, connected to the server and receiving real-time updates.\n */\n public readonly active: Signal<boolean>;\n\n /**\n * Whether the subscription is currently enabled.\n *\n * This property starts as `true` for non-lazy subscriptions and `false` for lazy subscriptions.\n *\n * Calling `execute()` sets it to `true`, while calling `terminate()` sets it to `false`.\n *\n * When `true`:\n * - The subscription automatically starts when variables change from `null` to a non-null value\n * - Variable changes trigger re-subscription with the new variables\n *\n * When `false`:\n * - Variable changes are ignored and do not trigger re-subscription\n * - The subscription must be manually started via `execute()`\n *\n * Note: This is different from `active`, which indicates whether the subscription is currently connected to the server and receiving real-time updates.\n */\n public readonly enabled: Signal<boolean>;\n\n private subscription: Subscription | undefined;\n private readonly _active: WritableSignal<boolean>;\n private readonly _result: WritableSignal<SignalSubscriptionResult<TData>>;\n private readonly _enabled: WritableSignal<boolean>;\n\n public constructor(\n injector: Injector,\n private readonly apollo: Apollo,\n private readonly options: SignalSubscriptionOptions<TData, TVariables>\n ) {\n const { variables, lazy = false } = options;\n\n this._enabled = signal(!lazy);\n this.enabled = this._enabled.asReadonly();\n\n this._active = signal(false);\n this.active = this._active.asReadonly();\n\n this._result = signal({ loading: false, data: undefined, error: undefined });\n this.result = this._result.asReadonly();\n\n this.loading = computed(() => this.result().loading);\n this.data = computed(() => this.result().data);\n this.error = computed(() => this.result().error);\n this.variables = variables !== undefined ? linkedSignal(variables, { equal }) : signal(variables);\n\n effect(() => {\n const variables = this.variables();\n const enabled = untracked(this.enabled);\n const active = untracked(this.active);\n\n if (!enabled) return;\n\n if (variables !== null) {\n this._execute({ variables });\n } else if (active) {\n this._terminate();\n }\n }, { injector });\n\n effect(() => {\n const variables = this.variables();\n if (untracked(this.active) && variables !== null) {\n this.execute({ variables });\n }\n }, { injector });\n\n injector.get(DestroyRef).onDestroy(() => this.terminate());\n }\n\n /**\n * Execute subscription.\n */\n public execute(execOptions: SignalSubscriptionExecOptions<TVariables> = {}): void {\n this._enabled.set(true);\n this._execute(execOptions);\n }\n\n /**\n * Terminate subscription.\n */\n public terminate(): void {\n this._enabled.set(false);\n this._terminate();\n }\n\n private _execute(execOptions: SignalSubscriptionExecOptions<TVariables> = {}): void {\n if ('variables' in execOptions) {\n this.variables.set(execOptions.variables);\n }\n\n const variables = untracked(this.variables);\n\n if (variables === null) {\n return;\n }\n\n this._active.set(true);\n this._result.set({\n loading: true,\n data: undefined,\n error: undefined\n });\n\n const { subscription, onData, onError, onComplete, injector, lazy, ...options } = this.options;\n this.subscription?.unsubscribe();\n this.subscription = this.apollo.subscribe<TData, TVariables>({\n ...options,\n ...execOptions,\n subscription,\n variables\n } as SubscriptionOptions<TData, TVariables>).subscribe({\n next: result => {\n this._result.set({\n loading: false,\n ...result\n });\n\n if (result.error) {\n onError?.(result.error);\n } else if (result.data !== undefined) {\n onData?.(result.data);\n }\n },\n // error is never called for subscriptions in Apollo Client\n complete: () => {\n this.terminate();\n onComplete?.();\n }\n });\n }\n\n private _terminate(): void {\n this._active.set(false);\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n this._result.update(result => ({ ...result, loading: false }));\n }\n}\n","import { assertInInjectionContext, inject, Injector } from '@angular/core';\nimport { DocumentNode, TypedDocumentNode, OperationVariables as Variables } from '@apollo/client';\nimport { DeepPartial } from '@apollo/client/utilities';\nimport type { Apollo } from '../apollo';\nimport { SignalCacheQuery, SignalCacheQueryOptions } from './cacheQuery';\nimport { SignalFragment, SignalFragmentOptions } from './fragment';\nimport { SignalMutation, SignalMutationOptions } from './mutation';\nimport { SignalQuery, SignalQueryOptions } from './query';\nimport { SignalSubscription, SignalSubscriptionOptions } from './subscription';\n\nexport class ApolloSignal {\n public constructor(\n private readonly apollo: Apollo\n ) { }\n\n public query<\n TData = unknown,\n TVariables extends Variables = Variables\n >(\n options: SignalQueryOptions<TData, TVariables> & { returnPartialData: true }\n ): SignalQuery<TData, TVariables, 'empty' | 'complete' | 'streaming' | 'partial'>;\n\n public query<\n TData = unknown,\n TVariables extends Variables = Variables\n >(\n options: SignalQueryOptions<TData, TVariables>\n ): SignalQuery<TData, TVariables, 'empty' | 'complete' | 'streaming'>;\n\n public query<\n TData = unknown,\n TVariables extends Variables = Variables\n >(options: SignalQueryOptions<TData, TVariables>): SignalQuery<TData, TVariables, any> {\n if (!options.injector) {\n assertInInjectionContext(this.query.bind(this));\n }\n\n const injector = options.injector ?? inject(Injector);\n\n return new SignalQuery<TData, TVariables>(\n injector,\n this.apollo,\n options\n );\n }\n\n public mutation<\n TData = unknown,\n TVariables extends Variables = Variables\n >(\n mutation: DocumentNode | TypedDocumentNode<TData, TVariables>,\n options?: SignalMutationOptions<TData, TVariables>\n ): SignalMutation<TData, TVariables> {\n return new SignalMutation<TData, TVariables>(\n this.apollo,\n mutation,\n options\n );\n }\n\n public subscription<\n TData = unknown,\n TVariables extends Variables = Variables\n >(options: SignalSubscriptionOptions<TData, TVariables>): SignalSubscription<TData, TVariables> {\n if (!options.injector) {\n assertInInjectionContext(this.subscription.bind(this));\n }\n\n const injector = options.injector ?? inject(Injector);\n\n return new SignalSubscription<TData, TVariables>(\n injector,\n this.apollo,\n options\n );\n }\n\n public fragment<\n TData = unknown,\n TVariables extends Variables = Variables\n >(options