Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 3.4 kB view raw
1import type { 2 AnyVariables, 3 Client, 4 CombinedError, 5 DocumentInput, 6 GraphQLRequest, 7 Operation, 8 OperationContext, 9 OperationResult, 10 OperationResultSource, 11} from '@urql/core'; 12import { createRequest } from '@urql/core'; 13import { type Ref, unref } from 'vue'; 14import { watchEffect, isReadonly, computed, ref, shallowRef, isRef } from 'vue'; 15import type { UseSubscriptionArgs } from './useSubscription'; 16import type { UseQueryArgs } from './useQuery'; 17 18export type MaybeRefOrGetter<T> = T | (() => T) | Ref<T>; 19export type MaybeRefOrGetterObj<T extends {}> = 20 T extends Record<string, never> 21 ? T 22 : { [K in keyof T]: MaybeRefOrGetter<T[K]> }; 23 24const isFunction = <T>(val: MaybeRefOrGetter<T>): val is () => T => 25 typeof val === 'function'; 26 27const toValue = <T>(source: MaybeRefOrGetter<T>): T => 28 isFunction(source) ? source() : unref(source); 29 30export const createRequestWithArgs = < 31 T = any, 32 V extends AnyVariables = AnyVariables, 33>( 34 args: 35 | UseQueryArgs<T, V> 36 | UseSubscriptionArgs<T, V> 37 | { query: MaybeRefOrGetter<DocumentInput<T, V>>; variables: V } 38): GraphQLRequest<T, V> => { 39 const _args = toValue(args); 40 return createRequest<T, V>( 41 toValue(_args.query), 42 toValue(_args.variables as MaybeRefOrGetter<V>) 43 ); 44}; 45 46export const useRequestState = < 47 T = any, 48 V extends AnyVariables = AnyVariables, 49>() => { 50 const hasNext: Ref<boolean> = ref(false); 51 const stale: Ref<boolean> = ref(false); 52 const fetching: Ref<boolean> = ref(false); 53 const error: Ref<CombinedError | undefined> = shallowRef(); 54 const operation: Ref<Operation<T, V> | undefined> = shallowRef(); 55 const extensions: Ref<Record<string, any> | undefined> = shallowRef(); 56 return { 57 hasNext, 58 stale, 59 fetching, 60 error, 61 operation, 62 extensions, 63 }; 64}; 65 66export function useClientState<T = any, V extends AnyVariables = AnyVariables>( 67 args: UseQueryArgs<T, V> | UseSubscriptionArgs<T, V>, 68 client: Ref<Client>, 69 method: keyof Pick<Client, 'executeSubscription' | 'executeQuery'> 70) { 71 const source: Ref<OperationResultSource<OperationResult<T, V>> | undefined> = 72 shallowRef(); 73 74 const isPaused: Ref<boolean> = isRef(args.pause) 75 ? args.pause 76 : typeof args.pause === 'function' 77 ? computed(args.pause) 78 : ref(!!args.pause); 79 80 const request = computed(() => createRequestWithArgs<T, V>(args)); 81 82 const requestOptions = computed(() => { 83 return 'requestPolicy' in args 84 ? { 85 requestPolicy: toValue(args.requestPolicy), 86 ...toValue(args.context), 87 } 88 : { 89 ...toValue(args.context), 90 }; 91 }); 92 93 const pause = () => { 94 if (!isReadonly(isPaused)) { 95 isPaused.value = true; 96 } 97 }; 98 99 const resume = () => { 100 if (!isReadonly(isPaused)) { 101 isPaused.value = false; 102 } 103 }; 104 105 const executeRaw = (opts?: Partial<OperationContext>) => { 106 return client.value[method]<T, V>(request.value, { 107 ...requestOptions.value, 108 ...opts, 109 }); 110 }; 111 112 const execute = (opts?: Partial<OperationContext>) => { 113 source.value = executeRaw(opts); 114 }; 115 116 // it's important to use `watchEffect()` here instead of `watch()` 117 // because it listening for reactive variables inside `executeRaw()` function 118 const teardown = watchEffect(() => { 119 source.value = !isPaused.value ? executeRaw() : undefined; 120 }); 121 122 return { 123 source, 124 isPaused, 125 pause, 126 resume, 127 execute, 128 teardown, 129 }; 130}