@speckle/apollo-composable
Version:
Apollo GraphQL for Vue Composition API
4 lines • 66.6 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/useQuery.ts", "../src/useApolloClient.ts", "../src/util/paramToRef.ts", "../src/util/paramToReactive.ts", "../src/util/useEventHook.ts", "../src/util/loadingTracking.ts", "../src/util/env.ts", "../src/util/toApolloError.ts", "../src/useLazyQuery.ts", "../src/useMutation.ts", "../src/useSubscription.ts", "../src/useResult.ts", "../src/useLoading.ts"],
"sourcesContent": ["import {\n ref,\n Ref,\n unref,\n computed,\n watch,\n onServerPrefetch,\n getCurrentScope,\n getCurrentInstance,\n onScopeDispose,\n nextTick,\n shallowRef,\n} from 'vue-demi'\nimport { DocumentNode } from 'graphql'\nimport type {\n OperationVariables,\n WatchQueryOptions,\n ObservableQuery,\n ApolloQueryResult,\n SubscribeToMoreOptions,\n FetchMoreQueryOptions,\n FetchMoreOptions,\n ObservableSubscription,\n TypedDocumentNode,\n ApolloError,\n ApolloClient,\n} from '@apollo/client/core/index.js'\nimport { throttle, debounce } from 'throttle-debounce'\nimport { useApolloClient } from './useApolloClient'\nimport { ReactiveFunction } from './util/ReactiveFunction'\nimport { paramToRef } from './util/paramToRef'\nimport { paramToReactive } from './util/paramToReactive'\nimport { useEventHook } from './util/useEventHook'\nimport { trackQuery } from './util/loadingTracking'\nimport { resultErrorsToApolloError, toApolloError } from './util/toApolloError'\nimport { isServer } from './util/env'\n\nexport interface UseQueryOptions<\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n TResult = any,\n TVariables extends OperationVariables = OperationVariables\n> extends Omit<WatchQueryOptions<TVariables>, 'query' | 'variables'> {\n clientId?: string\n enabled?: boolean | Ref<boolean>\n throttle?: number\n debounce?: number\n prefetch?: boolean\n keepPreviousResult?: boolean\n}\n\ninterface SubscribeToMoreItem {\n options: any\n unsubscribeFns: (() => void)[]\n}\n\n// Parameters\nexport type DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode | null | undefined> | ReactiveFunction<DocumentNode | null | undefined> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables> | null | undefined> | ReactiveFunction<TypedDocumentNode<TResult, TVariables> | null | undefined>\nexport type VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>\nexport type OptionsParameter<TResult, TVariables extends OperationVariables> = UseQueryOptions<TResult, TVariables> | Ref<UseQueryOptions<TResult, TVariables>> | ReactiveFunction<UseQueryOptions<TResult, TVariables>>\n\nexport interface OnResultContext {\n client: ApolloClient<any>\n}\n\nexport interface OnErrorContext {\n client: ApolloClient<any>\n}\n\n// Return\nexport interface UseQueryReturn<TResult, TVariables extends OperationVariables> {\n result: Ref<TResult | undefined>\n loading: Ref<boolean>\n networkStatus: Ref<number | undefined>\n error: Ref<ApolloError | null>\n start: () => void\n stop: () => void\n restart: () => void\n forceDisabled: Ref<boolean>\n document: Ref<DocumentNode | null | undefined>\n variables: Ref<TVariables | undefined>\n options: UseQueryOptions<TResult, TVariables> | Ref<UseQueryOptions<TResult, TVariables>>\n query: Ref<ObservableQuery<TResult, TVariables> | null | undefined>\n refetch: (variables?: TVariables) => Promise<ApolloQueryResult<TResult>> | undefined\n fetchMore: (options: FetchMoreQueryOptions<TVariables, TResult> & FetchMoreOptions<TResult, TVariables>) => Promise<ApolloQueryResult<TResult>> | undefined\n updateQuery: (mapFn: (previousQueryResult: TResult, options: Pick<WatchQueryOptions<TVariables, TResult>, 'variables'>) => TResult) => void\n subscribeToMore: <TSubscriptionVariables = OperationVariables, TSubscriptionData = TResult>(options: SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData> | Ref<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>> | ReactiveFunction<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>>) => void\n onResult: (fn: (param: ApolloQueryResult<TResult>, context: OnResultContext) => void) => {\n off: () => void\n }\n onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {\n off: () => void\n }\n}\n\n/**\n * Use a query that does not require variables or options.\n * */\nexport function useQuery<TResult = any> (\n document: DocumentParameter<TResult, undefined>\n): UseQueryReturn<TResult, Record<string, never>>\n\n/**\n * Use a query that has optional variables but not options\n */\nexport function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>\n): UseQueryReturn<TResult, TVariables>\n\n/**\n * Use a query that has required variables but not options\n */\nexport function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>,\n variables: VariablesParameter<TVariables>\n): UseQueryReturn<TResult, TVariables>\n\n/**\n * Use a query that requires options but not variables.\n */\nexport function useQuery<TResult = any> (\n document: DocumentParameter<TResult, undefined>,\n variables: undefined | null,\n options: OptionsParameter<TResult, Record<string, never>>,\n): UseQueryReturn<TResult, Record<string, never>>\n\n/**\n * Use a query that requires variables and options.\n */\nexport function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>,\n variables: VariablesParameter<TVariables>,\n options: OptionsParameter<TResult, TVariables>,\n): UseQueryReturn<TResult, TVariables>\n\nexport function useQuery<\n TResult,\n TVariables extends OperationVariables\n> (\n document: DocumentParameter<TResult, TVariables>,\n variables?: VariablesParameter<TVariables>,\n options?: OptionsParameter<TResult, TVariables>,\n): UseQueryReturn<TResult, TVariables> {\n return useQueryImpl<TResult, TVariables>(document, variables, options)\n}\n\nexport function useQueryImpl<\n TResult,\n TVariables extends OperationVariables\n> (\n document: DocumentParameter<TResult, TVariables>,\n variables?: VariablesParameter<TVariables>,\n options: OptionsParameter<TResult, TVariables> = {},\n lazy = false,\n): UseQueryReturn<TResult, TVariables> {\n const currentScope = getCurrentScope()\n const currentInstance = getCurrentInstance()\n\n const currentOptions = ref<UseQueryOptions<TResult, TVariables>>()\n\n const documentRef = paramToRef(document)\n const variablesRef = paramToRef(variables)\n const optionsRef = paramToReactive(options)\n\n // Result\n /**\n * Result from the query\n */\n const result = shallowRef<TResult | undefined>()\n const resultEvent = useEventHook<[ApolloQueryResult<TResult>, OnResultContext]>()\n const error = shallowRef<ApolloError | null>(null)\n const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()\n\n // Loading\n\n /**\n * Indicates if a network request is pending\n */\n const loading = ref(false)\n currentScope && trackQuery(loading)\n const networkStatus = ref<number>()\n\n // SSR\n let firstResolve: (() => void) | undefined\n let firstResolveTriggered = false\n let firstReject: ((apolloError: ApolloError) => void) | undefined\n let firstRejectError: undefined | ApolloError\n\n const tryFirstResolve = () => {\n firstResolveTriggered = true\n if (firstResolve) firstResolve()\n }\n\n const tryFirstReject = (apolloError: ApolloError) => {\n firstRejectError = apolloError\n if (firstReject) firstReject(apolloError)\n }\n\n const resetFirstResolveReject = () => {\n firstResolve = undefined\n firstReject = undefined\n firstResolveTriggered = false\n firstRejectError = undefined\n }\n\n currentInstance && onServerPrefetch?.(() => {\n if (!isEnabled.value || (isServer && currentOptions.value?.prefetch === false)) return\n\n return new Promise<void>((resolve, reject) => {\n firstResolve = () => {\n resetFirstResolveReject()\n resolve()\n }\n firstReject = (apolloError: ApolloError) => {\n resetFirstResolveReject()\n reject(apolloError)\n }\n\n if (firstResolveTriggered) {\n firstResolve()\n } else if (firstRejectError) {\n firstReject(firstRejectError)\n }\n }).finally(stop)\n })\n\n // Apollo Client\n const { resolveClient } = useApolloClient()\n\n function getClient () {\n return resolveClient(currentOptions.value?.clientId)\n }\n\n // Query\n\n const query: Ref<ObservableQuery<TResult, TVariables> | null | undefined> = shallowRef()\n let observer: ObservableSubscription | undefined\n let started = false\n let ignoreNextResult = false\n let firstStart = true\n\n /**\n * Starts watching the query\n */\n function start () {\n if (\n started || !isEnabled.value ||\n (isServer && currentOptions.value?.prefetch === false) ||\n !currentDocument\n ) {\n tryFirstResolve()\n return\n }\n\n // On server the watchers on document, variables and options are not triggered\n if (isServer) {\n applyDocument(documentRef.value)\n applyVariables(variablesRef.value)\n applyOptions(unref(optionsRef))\n }\n\n started = true\n error.value = null\n loading.value = true\n\n const client = getClient()\n\n query.value = client.watchQuery<TResult, TVariables>({\n query: currentDocument,\n variables: currentVariables ?? {} as TVariables,\n ...currentOptions.value,\n ...(isServer && currentOptions.value?.fetchPolicy !== 'no-cache')\n ? {\n fetchPolicy: 'network-only',\n }\n : {},\n })\n\n startQuerySubscription()\n\n // Make the cache data available to the component immediately\n // This prevents SSR hydration mismatches\n if (!isServer && (firstStart || !currentOptions.value?.keepPreviousResult) && (currentOptions.value?.fetchPolicy !== 'no-cache' || currentOptions.value.notifyOnNetworkStatusChange)) {\n const currentResult = query.value.getCurrentResult(false)\n\n if (!currentResult.loading || currentResult.partial || currentOptions.value?.notifyOnNetworkStatusChange) {\n onNextResult(currentResult)\n ignoreNextResult = !currentResult.loading\n } else if (currentResult.error) {\n onError(currentResult.error)\n ignoreNextResult = true\n }\n }\n\n if (!isServer) {\n for (const item of subscribeToMoreItems) {\n addSubscribeToMore(item)\n }\n }\n\n firstStart = false\n }\n\n function startQuerySubscription () {\n if (observer && !observer.closed) return\n if (!query.value) return\n\n // Create subscription\n ignoreNextResult = false\n observer = query.value.subscribe({\n next: onNextResult,\n error: onError,\n })\n }\n\n function getErrorPolicy () {\n const client = resolveClient(currentOptions.value?.clientId)\n return currentOptions.value?.errorPolicy || client.defaultOptions?.watchQuery?.errorPolicy\n }\n\n function onNextResult (queryResult: ApolloQueryResult<TResult>) {\n if (ignoreNextResult) {\n ignoreNextResult = false\n return\n }\n\n // Remove any previous error that may still be present from the last fetch (so result handlers\n // don't receive old errors that may not even be applicable anymore).\n error.value = null\n\n processNextResult(queryResult)\n\n // When `errorPolicy` is `all`, `onError` will not get called and\n // ApolloQueryResult.errors may be set at the same time as we get a result.\n // The code is only relevant when `errorPolicy` is `all`, because for other situations it\n // could hapen that next and error are called at the same time and then it will lead to multiple\n // onError calls.\n const errorPolicy = getErrorPolicy()\n if (errorPolicy && errorPolicy === 'all' && !queryResult.error && queryResult.errors?.length) {\n processError(resultErrorsToApolloError(queryResult.errors))\n }\n\n tryFirstResolve()\n }\n\n function processNextResult (queryResult: ApolloQueryResult<TResult>) {\n result.value = queryResult.data && Object.keys(queryResult.data).length === 0\n ? queryResult.error &&\n !currentOptions.value?.returnPartialData &&\n currentOptions.value?.errorPolicy === 'none'\n ? undefined\n : result.value\n : queryResult.data\n loading.value = queryResult.loading\n networkStatus.value = queryResult.networkStatus\n // Wait for handlers to be registered\n nextTick(() => {\n resultEvent.trigger(queryResult, {\n client: getClient(),\n })\n })\n }\n\n function onError (queryError: unknown) {\n if (ignoreNextResult) {\n ignoreNextResult = false\n return\n }\n\n // any error should already be an ApolloError, but we make sure\n const apolloError = toApolloError(queryError)\n const errorPolicy = getErrorPolicy()\n\n if (errorPolicy && errorPolicy !== 'none') {\n processNextResult((query.value as ObservableQuery<TResult, TVariables>).getCurrentResult())\n }\n processError(apolloError)\n tryFirstReject(apolloError)\n // The observable closes the sub if an error occurs\n resubscribeToQuery()\n }\n\n function processError (apolloError: ApolloError) {\n error.value = apolloError\n loading.value = false\n networkStatus.value = 8\n // Wait for handlers to be registered\n nextTick(() => {\n errorEvent.trigger(apolloError, {\n client: getClient(),\n })\n })\n }\n\n function resubscribeToQuery () {\n if (!query.value) return\n const lastError = query.value.getLastError()\n const lastResult = query.value.getLastResult()\n query.value.resetLastResults()\n startQuerySubscription()\n Object.assign(query.value, { lastError, lastResult })\n }\n\n let onStopHandlers: Array<() => void> = []\n\n /**\n * Stop watching the query\n */\n function stop () {\n tryFirstResolve()\n if (!started) return\n started = false\n loading.value = false\n\n onStopHandlers.forEach(handler => handler())\n onStopHandlers = []\n\n if (query.value) {\n query.value.stopPolling()\n query.value = null\n }\n\n if (observer) {\n observer.unsubscribe()\n observer = undefined\n }\n }\n\n // Restart\n let restarting = false\n /**\n * Queue a restart of the query (on next tick) if it is already active\n */\n function baseRestart () {\n if (!started || restarting) return\n restarting = true\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n nextTick(() => {\n if (started) {\n stop()\n start()\n }\n restarting = false\n })\n }\n\n let debouncedRestart: typeof baseRestart\n let isRestartDebounceSetup = false\n function updateRestartFn () {\n // On server, will be called before currentOptions is initialized\n // @TODO investigate\n if (!currentOptions.value) {\n debouncedRestart = baseRestart\n } else {\n if (currentOptions.value?.throttle) {\n debouncedRestart = throttle(currentOptions.value.throttle, baseRestart)\n } else if (currentOptions.value?.debounce) {\n debouncedRestart = debounce(currentOptions.value.debounce, baseRestart)\n } else {\n debouncedRestart = baseRestart\n }\n isRestartDebounceSetup = true\n }\n }\n\n function restart () {\n if (!started || restarting) return\n if (!isRestartDebounceSetup) updateRestartFn()\n debouncedRestart()\n }\n\n // Applying document\n let currentDocument: DocumentNode | null | undefined = documentRef.value\n\n // Enabled state\n\n const forceDisabled = ref(lazy)\n const enabledOption = computed(() => !currentOptions.value || currentOptions.value.enabled == null || currentOptions.value.enabled)\n const isEnabled = computed(() => enabledOption.value && !forceDisabled.value && !!documentRef.value)\n\n // Applying options first (in case it disables the query)\n watch(() => unref(optionsRef), applyOptions, {\n deep: true,\n immediate: true,\n })\n\n function applyOptions (value: UseQueryOptions<TResult, TVariables>) {\n if (currentOptions.value && (\n currentOptions.value.throttle !== value.throttle ||\n currentOptions.value.debounce !== value.debounce\n )) {\n updateRestartFn()\n }\n currentOptions.value = value\n restart()\n }\n\n // Applying document\n watch(documentRef, applyDocument)\n\n function applyDocument (value: DocumentNode | null | undefined) {\n currentDocument = value\n restart()\n }\n\n // Applying variables\n let currentVariables: TVariables | undefined\n let currentVariablesSerialized: string\n watch(() => {\n if (isEnabled.value) {\n return variablesRef.value\n } else {\n return undefined\n }\n }, applyVariables, {\n deep: true,\n immediate: true,\n })\n\n function applyVariables (value?: TVariables) {\n const serialized = JSON.stringify([value, isEnabled.value])\n if (serialized !== currentVariablesSerialized) {\n currentVariables = value\n restart()\n }\n currentVariablesSerialized = serialized\n }\n\n // Refetch\n\n function refetch (variables: TVariables | undefined = undefined) {\n if (query.value) {\n if (variables) {\n currentVariables = variables\n }\n error.value = null\n loading.value = true\n return query.value.refetch(variables)\n .then((refetchResult) => {\n const currentResult = query.value?.getCurrentResult()\n currentResult && processNextResult(currentResult)\n return refetchResult\n })\n }\n }\n\n // Update Query\n\n function updateQuery (mapFn: (previousQueryResult: TResult, options: Pick<WatchQueryOptions<TVariables, TResult>, 'variables'>) => TResult) {\n if (query.value) {\n query.value.updateQuery(mapFn)\n }\n }\n\n // Fetch more\n\n function fetchMore (options: FetchMoreQueryOptions<TVariables, TResult> & FetchMoreOptions<TResult, TVariables>) {\n if (query.value) {\n error.value = null\n loading.value = true\n return query.value.fetchMore(options)\n .then((fetchMoreResult) => {\n const currentResult = query.value?.getCurrentResult()\n currentResult && processNextResult(currentResult)\n return fetchMoreResult\n })\n }\n }\n\n // Subscribe to more\n\n const subscribeToMoreItems: SubscribeToMoreItem[] = []\n\n function subscribeToMore<\n TSubscriptionVariables = OperationVariables,\n TSubscriptionData = TResult\n > (\n options: SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData> |\n Ref<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>> |\n ReactiveFunction<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>>,\n ) {\n if (isServer) return\n const optionsRef = paramToRef(options)\n watch(optionsRef, (value, oldValue, onCleanup) => {\n const index = subscribeToMoreItems.findIndex(item => item.options === oldValue)\n if (index !== -1) {\n subscribeToMoreItems.splice(index, 1)\n }\n const item: SubscribeToMoreItem = {\n options: value,\n unsubscribeFns: [],\n }\n subscribeToMoreItems.push(item)\n\n addSubscribeToMore(item)\n\n onCleanup(() => {\n item.unsubscribeFns.forEach(fn => fn())\n item.unsubscribeFns = []\n })\n }, {\n immediate: true,\n })\n }\n\n function addSubscribeToMore (item: SubscribeToMoreItem) {\n if (!started) return\n if (!query.value) {\n throw new Error('Query is not defined')\n }\n const unsubscribe = query.value.subscribeToMore(item.options)\n onStopHandlers.push(unsubscribe)\n item.unsubscribeFns.push(unsubscribe)\n }\n\n // Auto start & stop\n\n watch(isEnabled, value => {\n if (value) {\n nextTick(() => {\n start()\n })\n } else {\n stop()\n }\n })\n\n if (isEnabled.value) {\n start()\n }\n\n // Teardown\n if (currentScope) {\n onScopeDispose(() => {\n stop()\n subscribeToMoreItems.length = 0\n })\n } else {\n console.warn('[Vue apollo] useQuery() is called outside of an active effect scope and the query will not be automatically stopped.')\n }\n\n return {\n result,\n loading,\n networkStatus,\n error,\n start,\n stop,\n restart,\n forceDisabled,\n document: documentRef,\n variables: variablesRef,\n options: optionsRef,\n query,\n refetch,\n fetchMore,\n subscribeToMore,\n updateQuery,\n onResult: resultEvent.on,\n onError: errorEvent.on,\n }\n}\n", "import { hasInjectionContext, inject } from 'vue-demi'\nimport { ApolloClient } from '@apollo/client/core/index.js'\n\nexport const DefaultApolloClient = Symbol('default-apollo-client')\nexport const ApolloClients = Symbol('apollo-clients')\n\ntype ClientId = string\ntype ClientDict<T> = Record<ClientId, ApolloClient<T>>\n\ntype ResolveClient<TCacheShape, TReturn = ApolloClient<TCacheShape>> = (clientId?: ClientId) => TReturn\ntype NullableApolloClient<TCacheShape> = ApolloClient<TCacheShape> | undefined\n\nexport interface UseApolloClientReturn<TCacheShape> {\n resolveClient: ResolveClient<TCacheShape>\n readonly client: ApolloClient<TCacheShape>\n}\n\nfunction resolveDefaultClient<T> (providedApolloClients: ClientDict<T> | null, providedApolloClient: ApolloClient<T> | null): NullableApolloClient<T> {\n const resolvedClient = providedApolloClients\n ? providedApolloClients.default\n : (providedApolloClient ?? undefined)\n return resolvedClient\n}\n\nfunction resolveClientWithId<T> (providedApolloClients: ClientDict<T> | null, clientId: ClientId): NullableApolloClient<T> {\n return providedApolloClients?.[clientId]\n}\n\nexport function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApolloClientReturn<TCacheShape> {\n let resolveImpl: ResolveClient<TCacheShape, NullableApolloClient<TCacheShape>>\n\n // Save current client in current closure scope\n const savedCurrentClients = currentApolloClients\n\n if (!hasInjectionContext()) {\n resolveImpl = (id?: ClientId) => {\n if (id) {\n return resolveClientWithId(savedCurrentClients, id)\n }\n return resolveDefaultClient(savedCurrentClients, savedCurrentClients.default)\n }\n } else {\n const providedApolloClients: ClientDict<TCacheShape> | null = inject(ApolloClients, null)\n const providedApolloClient: ApolloClient<TCacheShape> | null = inject(DefaultApolloClient, null)\n\n resolveImpl = (id?: ClientId) => {\n if (id) {\n const client = resolveClientWithId(providedApolloClients, id)\n if (client) {\n return client\n }\n return resolveClientWithId(savedCurrentClients, id)\n }\n const client = resolveDefaultClient(providedApolloClients, providedApolloClient)\n if (client) {\n return client\n }\n return resolveDefaultClient(savedCurrentClients, savedCurrentClients.default)\n }\n }\n\n function resolveClient (id: ClientId | undefined = clientId) {\n const client = resolveImpl(id)\n if (!client) {\n throw new Error(\n `Apollo client with id ${\n id ?? 'default'\n } not found. Use an app.runWithContext() or provideApolloClient() if you are outside of a component setup.`,\n )\n }\n return client\n }\n\n return {\n resolveClient,\n get client () {\n return resolveClient()\n },\n }\n}\n\nlet currentApolloClients: ClientDict<any> = {}\n\nexport function provideApolloClient<TCacheShape = any> (client: ApolloClient<TCacheShape>) {\n currentApolloClients = {\n default: client,\n }\n return function <TFnResult = any> (fn: () => TFnResult) {\n const result = fn()\n currentApolloClients = {}\n return result\n }\n}\n\nexport function provideApolloClients<TCacheShape = any> (clients: ClientDict<TCacheShape>) {\n currentApolloClients = clients\n return function <TFnResult = any> (fn: () => TFnResult) {\n const result = fn()\n currentApolloClients = {}\n return result\n }\n}\n", "import { Ref, isRef, computed, ref } from 'vue-demi'\nimport { ReactiveFunction } from './ReactiveFunction'\n\nexport function paramToRef<T> (param: T | Ref<T> | ReactiveFunction<T>): Ref<T> {\n if (isRef(param)) {\n return param\n } else if (typeof param === 'function') {\n return computed(param as ReactiveFunction<T>)\n } else {\n return ref(param) as Ref<T>\n }\n}\n", "import { Ref, isRef, reactive, computed } from 'vue-demi'\nimport { ReactiveFunction } from './ReactiveFunction'\n\n// eslint-disable-next-line @typescript-eslint/ban-types\ntype TObject = object\n\nexport function paramToReactive<T extends TObject> (param: T | Ref<T> | ReactiveFunction<T>): T | Ref<T> {\n if (isRef(param)) {\n return param\n } else if (typeof param === 'function') {\n return computed(param as ReactiveFunction<T>)\n } else if (param) {\n return reactive(param) as T\n } else {\n return param\n }\n}\n", "export function useEventHook<TParams extends any[] = any[]> () {\n const fns: Array<(...params: TParams) => void> = []\n\n function on (fn: (...params: TParams) => void) {\n fns.push(fn)\n return {\n off: () => off(fn),\n }\n }\n\n function off (fn: (...params: TParams) => void) {\n const index = fns.indexOf(fn)\n if (index !== -1) {\n fns.splice(index, 1)\n }\n }\n\n function trigger (...params: TParams) {\n for (const fn of fns) {\n fn(...params)\n }\n }\n\n function getCount () {\n return fns.length\n }\n\n return {\n on,\n off,\n trigger,\n getCount,\n }\n}\n", "import { Ref, watch, ref, getCurrentScope, onScopeDispose } from 'vue-demi'\nimport { isServer } from './env.js'\n\nimport type { EffectScope } from 'vue-demi'\n\nexport interface LoadingTracking {\n queries: Ref<number>\n mutations: Ref<number>\n subscriptions: Ref<number>\n}\n\nexport interface AppLoadingTracking extends LoadingTracking {\n components: Map<EffectScope, LoadingTracking>\n}\n\nexport const globalTracking: AppLoadingTracking = {\n queries: ref(0),\n mutations: ref(0),\n subscriptions: ref(0),\n components: new Map(),\n}\n\nexport function getCurrentTracking () {\n const currentScope = getCurrentScope()\n if (!currentScope) {\n return {}\n }\n\n let tracking: LoadingTracking\n if (isServer) {\n // SSR does not support onScopeDispose, so if we don't skip this, it will leak memory\n tracking = {\n queries: ref(0),\n mutations: ref(0),\n subscriptions: ref(0),\n }\n return { tracking }\n }\n\n if (!globalTracking.components.has(currentScope)) {\n // Add per-component tracking\n globalTracking.components.set(currentScope, tracking = {\n queries: ref(0),\n mutations: ref(0),\n subscriptions: ref(0),\n })\n // Cleanup\n onScopeDispose(() => {\n globalTracking.components.delete(currentScope)\n })\n } else {\n tracking = globalTracking.components.get(currentScope) as LoadingTracking\n }\n\n return {\n tracking,\n }\n}\n\nfunction track (loading: Ref<boolean>, type: keyof LoadingTracking) {\n if (isServer) return\n\n const { tracking } = getCurrentTracking()\n\n watch(loading, (value, oldValue) => {\n if (oldValue != null && value !== oldValue) {\n const mod = value ? 1 : -1\n if (tracking) tracking[type].value += mod\n globalTracking[type].value += mod\n }\n }, {\n immediate: true,\n })\n\n onScopeDispose(() => {\n if (loading.value) {\n if (tracking) tracking[type].value--\n globalTracking[type].value--\n }\n })\n}\n\nexport function trackQuery (loading: Ref<boolean>) {\n track(loading, 'queries')\n}\n\nexport function trackMutation (loading: Ref<boolean>) {\n track(loading, 'mutations')\n}\n\nexport function trackSubscription (loading: Ref<boolean>) {\n track(loading, 'subscriptions')\n}\n", "export const isServer = typeof window === 'undefined'\n", "import { ApolloError, isApolloError } from '@apollo/client/core/index.js'\nimport { GraphQLErrors } from '@apollo/client/errors/index.js'\n\nexport function toApolloError (error: unknown): ApolloError {\n if (!(error instanceof Error)) {\n return new ApolloError({\n networkError: Object.assign(new Error(), { originalError: error }),\n errorMessage: String(error),\n })\n }\n\n if (isApolloError(error)) {\n return error\n }\n\n return new ApolloError({ networkError: error, errorMessage: error.message })\n}\n\nexport function resultErrorsToApolloError (errors: GraphQLErrors): ApolloError {\n return new ApolloError({\n graphQLErrors: errors,\n errorMessage: `GraphQL response contains errors: ${errors.map((e: any) => e.message).join(' | ')}`,\n })\n}\n", "import { DocumentNode } from 'graphql'\nimport { isRef } from 'vue-demi'\nimport { useQueryImpl, DocumentParameter, VariablesParameter, OptionsParameter, UseQueryOptions, UseQueryReturn } from './useQuery'\nimport type { OperationVariables } from '@apollo/client/core'\nimport { isServer } from './util/env.js'\n\nexport interface UseLazyQueryReturn<TResult, TVariables extends OperationVariables> extends UseQueryReturn<TResult, TVariables> {\n /**\n * Activate the query and starts loading.\n * @param document Override document\n * @param variables Override variables\n * @param options Override options\n * @returns Returns false if the query is already active, otherwise the next result of the query.\n */\n load: (document?: DocumentNode | null, variables?: TVariables | null, options?: UseQueryOptions | null) => false | Promise<TResult>\n}\n\nexport function useLazyQuery<\n TResult = any,\n TVariables extends Record<string, unknown> = any,\n> (\n document: DocumentParameter<TResult, TVariables>,\n variables?: VariablesParameter<TVariables>,\n options?: OptionsParameter<TResult, TVariables>,\n): UseLazyQueryReturn<TResult, TVariables> {\n const query = useQueryImpl<TResult, TVariables>(document, variables, options, true)\n\n function load (\n document?: DocumentNode | null,\n variables?: TVariables | null,\n options?: UseQueryOptions | null,\n ) {\n if (document) {\n query.document.value = document\n }\n if (variables) {\n query.variables.value = variables\n }\n if (options) {\n Object.assign(isRef(query.options) ? query.options.value : query.options, options)\n }\n const isFirstRun = query.forceDisabled.value\n if (isFirstRun) {\n query.forceDisabled.value = false\n\n // If SSR, we need to start the query manually since `watch` on `isEnabled` in `useQueryImpl` won't be called.\n if (isServer) {\n query.start()\n }\n\n return new Promise<TResult>((resolve, reject) => {\n const { off: offResult } = query.onResult((result) => {\n if (!result.loading) {\n resolve(result.data)\n offResult()\n offError()\n }\n })\n const { off: offError } = query.onError((error) => {\n reject(error)\n offResult()\n offError()\n })\n })\n } else {\n return false\n }\n }\n\n return {\n ...query,\n load,\n }\n}\n", "import { DocumentNode } from 'graphql'\nimport { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode, ApolloError, ApolloClient } from '@apollo/client/core/index.js'\nimport { ref, onScopeDispose, isRef, Ref, getCurrentScope, shallowRef, nextTick } from 'vue-demi'\nimport { useApolloClient } from './useApolloClient'\nimport { ReactiveFunction } from './util/ReactiveFunction'\nimport { useEventHook } from './util/useEventHook'\nimport { trackMutation } from './util/loadingTracking'\nimport { toApolloError } from './util/toApolloError'\n\n/**\n * `useMutation` options for mutations that don't require `variables`.\n */\nexport interface UseMutationOptions<\n TResult = any,\n TVariables = OperationVariables\n> extends Omit<MutationOptions<TResult, TVariables>, 'mutation'> {\n clientId?: string\n throws?: 'auto' | 'always' | 'never'\n}\n\ntype DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables>> | ReactiveFunction<TypedDocumentNode<TResult, TVariables>>\ntype OptionsParameter<TResult, TVariables> = UseMutationOptions<TResult, TVariables> | Ref<UseMutationOptions<TResult, TVariables>> | ReactiveFunction<UseMutationOptions<TResult, TVariables>>\n\nexport type MutateOverrideOptions<TResult> = Pick<UseMutationOptions<TResult, OperationVariables>, 'update' | 'optimisticResponse' | 'context' | 'updateQueries' | 'refetchQueries' | 'awaitRefetchQueries' | 'errorPolicy' | 'fetchPolicy' | 'clientId'>\nexport type MutateResult<TResult> = Promise<FetchResult<TResult, Record<string, any>, Record<string, any>> | null>\nexport type MutateFunction<TResult, TVariables> = (variables?: TVariables | null, overrideOptions?: MutateOverrideOptions<TResult>) => MutateResult<TResult>\n\nexport interface OnDoneContext {\n client: ApolloClient<any>\n}\n\nexport interface OnErrorContext {\n client: ApolloClient<any>\n}\n\nexport interface UseMutationReturn<TResult, TVariables> {\n mutate: MutateFunction<TResult, TVariables>\n loading: Ref<boolean>\n error: Ref<ApolloError | null>\n called: Ref<boolean>\n onDone: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>, context: OnDoneContext) => void) => {\n off: () => void\n }\n onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {\n off: () => void\n }\n}\n\nexport function useMutation<\n TResult = any,\n TVariables extends OperationVariables = OperationVariables\n> (\n document: DocumentParameter<TResult, TVariables>,\n options: OptionsParameter<TResult, TVariables> = {},\n): UseMutationReturn<TResult, TVariables> {\n const currentScope = getCurrentScope()\n const loading = ref<boolean>(false)\n currentScope && trackMutation(loading)\n const error = shallowRef<ApolloError | null>(null)\n const called = ref<boolean>(false)\n\n const doneEvent = useEventHook<[FetchResult<TResult, Record<string, any>, Record<string, any>>, OnDoneContext]>()\n const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()\n\n // Apollo Client\n const { resolveClient } = useApolloClient()\n\n async function mutate (variables?: TVariables | null, overrideOptions: Omit<UseMutationOptions<TResult, TVariables>, 'variables'> = {}) {\n let currentDocument: DocumentNode\n if (typeof document === 'function') {\n currentDocument = document()\n } else if (isRef(document)) {\n currentDocument = document.value\n } else {\n currentDocument = document\n }\n\n let currentOptions: UseMutationOptions<TResult, TVariables>\n if (typeof options === 'function') {\n currentOptions = options()\n } else if (isRef(options)) {\n currentOptions = options.value\n } else {\n currentOptions = options\n }\n const client = resolveClient(currentOptions.clientId)\n error.value = null\n loading.value = true\n called.value = true\n try {\n const result = await client.mutate<TResult, TVariables>({\n mutation: currentDocument,\n ...currentOptions,\n ...overrideOptions,\n variables: (variables ?? currentOptions.variables)\n ? {\n ...(currentOptions.variables as TVariables),\n ...(variables as TVariables),\n }\n : undefined,\n })\n loading.value = false\n await nextTick()\n doneEvent.trigger(result, {\n client,\n })\n return result\n } catch (e) {\n const apolloError = toApolloError(e)\n error.value = apolloError\n loading.value = false\n errorEvent.trigger(apolloError, {\n client,\n })\n if (currentOptions.throws === 'always' || (currentOptions.throws !== 'never' && !errorEvent.getCount())) {\n throw apolloError\n }\n }\n return null\n }\n\n currentScope && onScopeDispose(() => {\n loading.value = false\n })\n\n return {\n mutate,\n loading,\n error,\n called,\n onDone: doneEvent.on,\n onError: errorEvent.on,\n }\n}\n", "import { DocumentNode } from 'graphql'\nimport {\n Ref,\n ref,\n watch,\n isRef,\n computed,\n getCurrentScope,\n onScopeDispose,\n nextTick,\n shallowRef,\n} from 'vue-demi'\nimport type {\n OperationVariables,\n SubscriptionOptions,\n FetchResult,\n Observable,\n ObservableSubscription,\n TypedDocumentNode,\n ApolloError,\n ApolloClient,\n} from '@apollo/client/core/index.js'\nimport { throttle, debounce } from 'throttle-debounce'\nimport { ReactiveFunction } from './util/ReactiveFunction'\nimport { paramToRef } from './util/paramToRef'\nimport { paramToReactive } from './util/paramToReactive'\nimport { useApolloClient } from './useApolloClient'\nimport { useEventHook } from './util/useEventHook'\nimport { trackSubscription } from './util/loadingTracking'\nimport { toApolloError } from './util/toApolloError'\nimport { isServer } from './util/env'\n\nexport interface UseSubscriptionOptions <\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n TResult = any,\n TVariables = OperationVariables\n> extends Omit<SubscriptionOptions<TVariables>, 'query' | 'variables'> {\n clientId?: string\n enabled?: boolean | Ref<boolean>\n throttle?: number\n debounce?: number\n}\n\ntype DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables>> | ReactiveFunction<TypedDocumentNode<TResult, TVariables>>\ntype VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>\ntype OptionsParameter<TResult, TVariables> = UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>> | ReactiveFunction<UseSubscriptionOptions<TResult, TVariables>>\n\nexport interface OnResultContext {\n client: ApolloClient<any>\n}\n\nexport interface OnErrorContext {\n client: ApolloClient<any>\n}\n\nexport interface UseSubscriptionReturn<TResult, TVariables> {\n result: Ref<TResult | null | undefined>\n loading: Ref<boolean>\n error: Ref<ApolloError | null>\n start: () => void\n stop: () => void\n restart: () => void\n document: Ref<DocumentNode>\n variables: Ref<TVariables | undefined>\n options: UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>>\n subscription: Ref<Observable<FetchResult<TResult, Record<string, any>, Record<string, any>>> | null>\n onResult: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>, context: OnResultContext) => void) => {\n off: () => void\n }\n onError: (fn: (param: ApolloError, context: OnErrorContext) => void) => {\n off: () => void\n }\n}\n\n/**\n * Use a subscription that does not require variables or options.\n * */\nexport function useSubscription<TResult = any> (\n document: DocumentParameter<TResult, undefined>\n): UseSubscriptionReturn<TResult, undefined>\n\n/**\n * Use a subscription that requires options but not variables.\n */\nexport function useSubscription<TResult = any> (\n document: DocumentParameter<TResult, undefined>,\n variables: undefined | null,\n options: OptionsParameter<TResult, null>\n): UseSubscriptionReturn<TResult, null>\n\n/**\n * Use a subscription that requires variables.\n */\nexport function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>,\n variables: VariablesParameter<TVariables>\n): UseSubscriptionReturn<TResult, TVariables>\n\n/**\n * Use a subscription that has optional variables.\n */\nexport function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>,\n): UseSubscriptionReturn<TResult, TVariables>\n\n/**\n * Use a subscription that requires variables and options.\n */\nexport function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (\n document: DocumentParameter<TResult, TVariables>,\n variables: VariablesParameter<TVariables>,\n options: OptionsParameter<TResult, TVariables>\n): UseSubscriptionReturn<TResult, TVariables>\n\nexport function useSubscription <\n TResult,\n TVariables extends Record<string, unknown>\n> (\n document: DocumentParameter<TResult, TVariables>,\n variables: VariablesParameter<TVariables> | undefined = undefined,\n options: OptionsParameter<TResult, TVariables> = {},\n): UseSubscriptionReturn<TResult, TVariables> {\n const currentScope = getCurrentScope()\n\n const documentRef = paramToRef(document)\n const variablesRef = paramToRef(variables)\n const optionsRef = paramToReactive(options)\n\n const result = shallowRef<TResult | null | undefined>()\n const resultEvent = useEventHook<[FetchResult<TResult>, OnResultContext]>()\n const error = shallowRef<ApolloError | null>(null)\n const errorEvent = useEventHook<[ApolloError, OnErrorContext]>()\n\n const loading = ref(false)\n currentScope && trackSubscription(loading)\n\n // Apollo Client\n const { resolveClient } = useApolloClient()\n\n const subscription: Ref<Observable<FetchResult<TResult>> | null> = ref(null)\n let observer: ObservableSubscription | null = null\n let started = false\n\n function getClient () {\n return resolveClient(currentOptions.value?.clientId)\n }\n\n function start () {\n if (started || !isEnabled.value || isServer) return\n started = true\n loading.value = true\n\n const client = getClient()\n\n subscription.value = client.subscribe<TResult, TVariables>({\n query: currentDocument,\n variables: currentVariables,\n ...currentOptions.value,\n })\n\n observer = subscription.value.subscribe({\n next: onNextResult,\n error: onError,\n })\n }\n\n function onNextResult (fetchResult: FetchResult<TResult>) {\n result.value = fetchResult.data\n loading.value = false\n resultEvent.trigger(fetchResult, {\n client: getClient(),\n })\n }\n\n function onError (fetchError: unknown) {\n const apolloError = toApolloError(fetchError)\n\n error.value = apolloError\n loading.value = false\n errorEvent.trigger(apolloError, {\n client: getClient(),\n })\n }\n\n function stop () {\n if (!started) return\n started = false\n loading.value = false\n\n if (subscription.value) {\n subscription.value = null\n }\n\n if (observer) {\n observer.unsubscribe()\n observer = null\n }\n }\n\n // Restart\n let restarting = false\n /**\n * Queue a restart of the query (on next tick) if it is already active\n */\n function baseRestart () {\n if (!started || restarting) return\n restarting = true\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n nextTick(() => {\n if (started) {\n stop()\n start()\n }\n restarting = false\n })\n }\n\n let debouncedRestart: typeof baseRestart\n function updateRestartFn () {\n if (currentOptions.value?.throttle) {\n debouncedRestart = throttle(currentOptions.value.throttle, baseRestart)\n } else if (currentOptions.value?.debounce) {\n debouncedRestart = debounce(currentOptions.value.debounce, baseRestart)\n } else {\n debouncedRestart = baseRestart\n }\n }\n\n function restart () {\n if (!debouncedRestart) updateRestartFn()\n debouncedRestart()\n }\n\n // Applying options\n const currentOptions = ref<UseSubscriptionOptions<TResult, TVariables>>()\n watch(() => isRef(optionsRef) ? optionsRef.value : optionsRef, value => {\n if (currentOptions.value && (\n currentOptions.value.throttle !== value.throttle ||\n currentOptions.value.debounce !== value.debounce\n )) {\n updateRestartFn()\n }\n currentOptions.value = value\n restart()\n }, {\n deep: true,\n immediate: true,\n })\n\n // Applying document\n let currentDocument: DocumentNode\n watch(documentRef, value => {\n currentDocument = value\n restart()\n }, {\n immediate: true,\n })\n\n // Applying variables\n let currentVariables: TVariables | undefined\n let currentVariablesSerialized: string\n watch(variablesRef, (value, oldValue) => {\n const serialized = JSON.stringify(value)\n if (serialized !== currentVariablesSerialized) {\n currentVariables = value\n restart()\n }\n currentVariablesSerialized = serialized\n }, {\n deep: true,\n immediate: true,\n })\n\n // Internal enabled returned to user\n // @TODO Doesn't fully work yet, need to initialize with option\n // const enabled = ref<boolean>()\n const enabledOption = computed(() => !currentOptions.value || currentOptions.value.enabled == null || currentOptions.value.enabled)\n // const isEnabled = computed(() => !!((typeof enabled.value === 'boolean' && enabled.value) && enabledOption.value))\n const isEnabled = enabledOption\n\n // watch(enabled, value => {\n // if (value == null) {\n // enabled.value = enabledOption.value\n // }\n // })\n\n // Auto start & stop\n watch(isEnabled, value => {\n if (value) {\n start()\n } else {\n stop()\n }\n }, {\n immediate: true,\n })\n\n // Teardown\n if (currentScope) {\n onScopeDispose(stop)\n } else {\n console.warn('[Vue apollo] useSubscription() is called outside of an active effect scope and the subscription will not be automatically stopped.')\n }\n\n return {\n result,\n loading,\n error,\n // @TODO doesn't fully work yet\n // enabled,\n start,\n stop,\n restart,\n document: documentRef,\n variables: variablesRef,\n options: optionsRef,\n subscription,\n onResult: resultEvent.on,\n onError: errorEvent.on,\n }\n}\n", "import { Ref, computed } from 'vue-demi'\nimport { ExtractSingleKey } from './util/ExtractSingleKey'\nimport type { DeepNonNullable, DeepRequired } from 'ts-essentials'\n\nexport type UseResultReturn<T> = Readonly<Ref<Readonly<T>>>\n\n/**\n * Resolve a `result`, returning either the first key of the `result` if there\n * is only one, or the `result` itself. The `value` of the ref will be\n * `undefined` until it is resolved.\n *\n * @example\n * const { result } = useQuery(...)\n * const user = useResult(result)\n * // user is `undefined` until the query resolves\n *\n * @param {Ref<TResult>} result A `result` returned from `useQuery` to resolve.\n * @returns Readonly ref with `undefined` or the resolved `result`.\n * @deprecated Use `computed` instead. Before: `const items = useResult(result, [], data => data.someField.myItems)` After: `const items = computed(() => result.value?.someField.myItems ?? [])`\n */\nexport function useResult<TResult, TResultKey extends keyof NonNullable<TResult> = keyof NonNullable<TResult>> (\n result: Ref<TResult>\n): UseResultReturn<undefined | ExtractSingleKey<NonNullable<TResult>, TResultKey>>\n\n/**\n * Resolve a `result`, returning either the first key of the `result` if there\n * is only one, or the `result` itself. The `value` of the ref will be\n * `defaultValue` until it is resolved.\n *\n * @example\n * const { result } = useQuery(...)\n * const profile = useResult(result, {})\n * // profile is `{}` until the query resolves\n *\n * @param {Ref<TResult>} result A `result` returned from `useQuery` to resolve.\n * @param {TDefaultValue} defaultValue The default return value before `result` is resolved.\n * @returns Readonly ref with the `defaultValue` or the resolved `result`.\n * @deprecated Use `computed` instead. Before: `const items = useResult(result, [], data => data.someField.myItems)` After: `const items = computed(() => result.value?.someField.myItems ?? [])`\n */\nexport function useResult<TResult, TDefaultValue, TResultKey extends keyof NonNullable<TResult> = keyof NonNullable<TResult>> (\n result: Ref<TResult>,\n defaultValue: TDefaultValue\n): UseResultReturn<TDefaultValue | ExtractSingleKey<NonNullable<TResult>, TResultKey>>\n\n/**\n * Resolve a `result`, returning the `result` mapped with the `pick` function.\n * The `value` of the ref will be `defaultValue` until it is resolved.\n *\n * @example\n * const { result } = useQuery(...)\n * const comments = useResult(result, undefined, (data) => data.comments)\n * // user is `undefined`, then resolves to the result's `comments`\n *\n * @param {Ref<TResult>} result A `result` returned from `useQuery` to resolve.\n * @param {TDefaultValue} defaultValue The default return value before `result` is resolved.\n * @param {(data:TResult)=>TReturnValue} pick The function that receives `result` and maps a return value from it.\n * @returns Readonly ref with the `defaultValue` or the resolved and `pick`-mapped `result`\n * @deprecated Use `computed` instead. Before: `const items = useResult(result, [], data => data.someField.myItems)` After: `const items = computed(() => result.value?.someField.myItems ?? [])`\n */\nexport function useResult<\n TResult,\n TDefaultValue,\n TReturnValue,\n> (\n result: Ref<TResult>,\n defaultValue: TDefaultValue | undefined,\n pick: (data: DeepRequired<DeepNonNullable<TResult>>) => TReturnValue\n): UseResultReturn<TDefaultValue | TReturnValue>\n\n/**\n * @deprecated Use `computed` instead. Before: `const items = u