import type { AnyVariables, Client, CombinedError, DocumentInput, GraphQLRequest, Operation, OperationContext, OperationResult, OperationResultSource, } from '@urql/core'; import { createRequest } from '@urql/core'; import { type Ref, unref } from 'vue'; import { watchEffect, isReadonly, computed, ref, shallowRef, isRef } from 'vue'; import type { UseSubscriptionArgs } from './useSubscription'; import type { UseQueryArgs } from './useQuery'; export type MaybeRefOrGetter = T | (() => T) | Ref; export type MaybeRefOrGetterObj = T extends Record ? T : { [K in keyof T]: MaybeRefOrGetter }; const isFunction = (val: MaybeRefOrGetter): val is () => T => typeof val === 'function'; const toValue = (source: MaybeRefOrGetter): T => isFunction(source) ? source() : unref(source); export const createRequestWithArgs = < T = any, V extends AnyVariables = AnyVariables, >( args: | UseQueryArgs | UseSubscriptionArgs | { query: MaybeRefOrGetter>; variables: V } ): GraphQLRequest => { const _args = toValue(args); return createRequest( toValue(_args.query), toValue(_args.variables as MaybeRefOrGetter) ); }; export const useRequestState = < T = any, V extends AnyVariables = AnyVariables, >() => { const hasNext: Ref = ref(false); const stale: Ref = ref(false); const fetching: Ref = ref(false); const error: Ref = shallowRef(); const operation: Ref | undefined> = shallowRef(); const extensions: Ref | undefined> = shallowRef(); return { hasNext, stale, fetching, error, operation, extensions, }; }; export function useClientState( args: UseQueryArgs | UseSubscriptionArgs, client: Ref, method: keyof Pick ) { const source: Ref> | undefined> = shallowRef(); const isPaused: Ref = isRef(args.pause) ? args.pause : typeof args.pause === 'function' ? computed(args.pause) : ref(!!args.pause); const request = computed(() => createRequestWithArgs(args)); const requestOptions = computed(() => { return 'requestPolicy' in args ? { requestPolicy: toValue(args.requestPolicy), ...toValue(args.context), } : { ...toValue(args.context), }; }); const pause = () => { if (!isReadonly(isPaused)) { isPaused.value = true; } }; const resume = () => { if (!isReadonly(isPaused)) { isPaused.value = false; } }; const executeRaw = (opts?: Partial) => { return client.value[method](request.value, { ...requestOptions.value, ...opts, }); }; const execute = (opts?: Partial) => { source.value = executeRaw(opts); }; // it's important to use `watchEffect()` here instead of `watch()` // because it listening for reactive variables inside `executeRaw()` function const teardown = watchEffect(() => { source.value = !isPaused.value ? executeRaw() : undefined; }); return { source, isPaused, pause, resume, execute, teardown, }; }