From 2c32fc2989358a296536379253442451bb19c8d2 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 9 Mar 2023 03:32:43 +0000 Subject: [PATCH 1/3] Add fixed conditional variables type --- .changeset/tricky-ears-add.md | 5 +++++ packages/core/src/types.ts | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .changeset/tricky-ears-add.md diff --git a/.changeset/tricky-ears-add.md b/.changeset/tricky-ears-add.md new file mode 100644 index 0000000000..a3ec7a882a --- /dev/null +++ b/.changeset/tricky-ears-add.md @@ -0,0 +1,5 @@ +--- +'@urql/core': patch +--- + +Expose consistent `GraphQLRequestParams` utility type from which `GraphQLRequest`s are created in all bindings. diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ce4ef5b3f3..c1376ca532 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -238,6 +238,43 @@ export interface GraphQLRequest< variables: Variables; } +/** Parameters from which {@link GraphQLRequest | GraphQLRequests} are created from. + * + * @remarks + * A `GraphQLRequest` is a single executable request with a generated `key` to identify + * their results, whereas `GraphQLRequestParams` is a utility type used to generate + * inputs for `urql` to create requests from, i.e. it only contains a `query` and + * `variables`. The type conditionally makes the `variables` property completely + * optional. + * + * @privateRemarks + * The wrapping union type is needed for passthrough or wrapper utilities that wrap + * functions like `useQuery` with generics. + */ +export type GraphQLRequestParams< + Data = any, + Variables extends AnyVariables = AnyVariables +> = + | ({ + query: string | DocumentNode | TypedDocumentNode; + } & (Variables extends void + ? { + variables?: Variables; + } + : Variables extends { + [P in keyof Variables]: Exclude; + } + ? { + variables: Variables; + } + : { + variables?: Variables; + })) + | { + query: string | DocumentNode | TypedDocumentNode; + variables: Variables; + }; + /** Metadata used to annotate an `Operation` in development for the `urql-devtools`. * * @remarks From a4433636b64a02c69567688aca14ebbc57bc96e5 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 9 Mar 2023 04:23:10 +0000 Subject: [PATCH 2/3] Fix optional variables parameters on input objects --- .changeset/slimy-shirts-bow.md | 8 +++++ packages/preact-urql/src/hooks/useQuery.ts | 14 ++------ .../preact-urql/src/hooks/useSubscription.ts | 14 ++------ packages/react-urql/src/hooks/useQuery.ts | 14 ++------ .../react-urql/src/hooks/useSubscription.ts | 14 ++------ packages/svelte-urql/src/mutationStore.ts | 19 +++-------- packages/svelte-urql/src/queryStore.ts | 16 +++------ packages/svelte-urql/src/subscriptionStore.ts | 16 +++------ packages/vue-urql/src/useQuery.ts | 34 ++++++++----------- packages/vue-urql/src/useSubscription.ts | 27 ++++++--------- 10 files changed, 54 insertions(+), 122 deletions(-) create mode 100644 .changeset/slimy-shirts-bow.md diff --git a/.changeset/slimy-shirts-bow.md b/.changeset/slimy-shirts-bow.md new file mode 100644 index 0000000000..d0f373ed8d --- /dev/null +++ b/.changeset/slimy-shirts-bow.md @@ -0,0 +1,8 @@ +--- +'@urql/preact': patch +'@urql/svelte': patch +'urql': patch +'@urql/vue': patch +--- + +Fix type utilities turning the `variables` properties optional when a type from `TypedDocumentNode` has no `Variables` or all optional `Variables`. Previously this would break for wrappers, e.g. in code generators, or when the type didn't quite match what we'd expect. diff --git a/packages/preact-urql/src/hooks/useQuery.ts b/packages/preact-urql/src/hooks/useQuery.ts index 618b922b45..6b15e2c2fc 100644 --- a/packages/preact-urql/src/hooks/useQuery.ts +++ b/packages/preact-urql/src/hooks/useQuery.ts @@ -1,4 +1,3 @@ -import { DocumentNode } from 'graphql'; import { useEffect, useCallback, useMemo } from 'preact/hooks'; import { @@ -15,8 +14,8 @@ import { import { Client, + GraphQLRequestParams, AnyVariables, - TypedDocumentNode, CombinedError, OperationContext, RequestPolicy, @@ -33,19 +32,10 @@ export type UseQueryArgs< Variables extends AnyVariables = AnyVariables, Data = any > = { - query: string | DocumentNode | TypedDocumentNode; requestPolicy?: RequestPolicy; context?: Partial; pause?: boolean; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export interface UseQueryState< Data = any, diff --git a/packages/preact-urql/src/hooks/useSubscription.ts b/packages/preact-urql/src/hooks/useSubscription.ts index d9756d9fa2..f8f80dcbd8 100644 --- a/packages/preact-urql/src/hooks/useSubscription.ts +++ b/packages/preact-urql/src/hooks/useSubscription.ts @@ -1,10 +1,9 @@ -import { DocumentNode } from 'graphql'; import { useEffect, useCallback, useRef, useMemo } from 'preact/hooks'; import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka'; import { AnyVariables, - TypedDocumentNode, + GraphQLRequestParams, CombinedError, OperationContext, Operation, @@ -19,18 +18,9 @@ export type UseSubscriptionArgs< Variables extends AnyVariables = AnyVariables, Data = any > = { - query: DocumentNode | TypedDocumentNode | string; pause?: boolean; context?: Partial; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export type SubscriptionHandler = (prev: R | undefined, data: T) => R; diff --git a/packages/react-urql/src/hooks/useQuery.ts b/packages/react-urql/src/hooks/useQuery.ts index a128d96c6c..1137d853a7 100644 --- a/packages/react-urql/src/hooks/useQuery.ts +++ b/packages/react-urql/src/hooks/useQuery.ts @@ -1,13 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { DocumentNode } from 'graphql'; import { Source, pipe, subscribe, onEnd, onPush, takeWhile } from 'wonka'; import { useState, useEffect, useCallback, useMemo } from 'react'; import { + GraphQLRequestParams, AnyVariables, Client, - TypedDocumentNode, CombinedError, OperationContext, RequestPolicy, @@ -24,19 +23,10 @@ export type UseQueryArgs< Variables extends AnyVariables = AnyVariables, Data = any > = { - query: string | DocumentNode | TypedDocumentNode; requestPolicy?: RequestPolicy; context?: Partial; pause?: boolean; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export interface UseQueryState< Data = any, diff --git a/packages/react-urql/src/hooks/useSubscription.ts b/packages/react-urql/src/hooks/useSubscription.ts index 75be9057b3..1f3f3b3708 100644 --- a/packages/react-urql/src/hooks/useSubscription.ts +++ b/packages/react-urql/src/hooks/useSubscription.ts @@ -1,12 +1,11 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { DocumentNode } from 'graphql'; import { pipe, subscribe, onEnd } from 'wonka'; import { useEffect, useState, useCallback, useMemo, useRef } from 'react'; import { + GraphQLRequestParams, AnyVariables, - TypedDocumentNode, CombinedError, OperationContext, Operation, @@ -20,18 +19,9 @@ export type UseSubscriptionArgs< Variables extends AnyVariables = AnyVariables, Data = any > = { - query: DocumentNode | TypedDocumentNode | string; pause?: boolean; context?: Partial; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export type SubscriptionHandler = (prev: R | undefined, data: T) => R; diff --git a/packages/svelte-urql/src/mutationStore.ts b/packages/svelte-urql/src/mutationStore.ts index 38c4918545..2548fd6100 100644 --- a/packages/svelte-urql/src/mutationStore.ts +++ b/packages/svelte-urql/src/mutationStore.ts @@ -1,13 +1,13 @@ -import type { DocumentNode } from 'graphql'; +import { pipe, map, scan, subscribe } from 'wonka'; +import { derived, writable } from 'svelte/store'; + import { AnyVariables, + GraphQLRequestParams, Client, OperationContext, - TypedDocumentNode, createRequest, } from '@urql/core'; -import { pipe, map, scan, subscribe } from 'wonka'; -import { derived, writable } from 'svelte/store'; import { OperationResultState, @@ -22,17 +22,8 @@ export type MutationArgs< Variables extends AnyVariables = AnyVariables > = { client: Client; - query: string | DocumentNode | TypedDocumentNode; context?: Partial; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export function mutationStore< Data = any, diff --git a/packages/svelte-urql/src/queryStore.ts b/packages/svelte-urql/src/queryStore.ts index 44bc89472e..49e0393bb3 100644 --- a/packages/svelte-urql/src/queryStore.ts +++ b/packages/svelte-urql/src/queryStore.ts @@ -1,12 +1,12 @@ -import type { DocumentNode } from 'graphql'; import { Client, + GraphQLRequestParams, AnyVariables, OperationContext, - TypedDocumentNode, RequestPolicy, createRequest, } from '@urql/core'; + import { Source, pipe, @@ -18,6 +18,7 @@ import { scan, never, } from 'wonka'; + import { derived, writable } from 'svelte/store'; import { @@ -34,19 +35,10 @@ export type QueryArgs< Variables extends AnyVariables = AnyVariables > = { client: Client; - query: string | DocumentNode | TypedDocumentNode; context?: Partial; requestPolicy?: RequestPolicy; pause?: boolean; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export function queryStore< Data = any, diff --git a/packages/svelte-urql/src/subscriptionStore.ts b/packages/svelte-urql/src/subscriptionStore.ts index 605900220e..6427592e80 100644 --- a/packages/svelte-urql/src/subscriptionStore.ts +++ b/packages/svelte-urql/src/subscriptionStore.ts @@ -1,11 +1,11 @@ -import type { DocumentNode } from 'graphql'; import { AnyVariables, + GraphQLRequestParams, Client, OperationContext, - TypedDocumentNode, createRequest, } from '@urql/core'; + import { Source, pipe, @@ -17,6 +17,7 @@ import { scan, never, } from 'wonka'; + import { derived, writable } from 'svelte/store'; import { @@ -33,18 +34,9 @@ export type SubscriptionArgs< Variables extends AnyVariables = AnyVariables > = { client: Client; - query: string | DocumentNode | TypedDocumentNode; context?: Partial; pause?: boolean; -} & (Variables extends void - ? { - variables?: Variables; - } - : Variables extends { [P in keyof Variables]: Variables[P] | null } - ? { variables?: Variables } - : { - variables: Variables; - }); +} & GraphQLRequestParams; export function subscriptionStore< Data, diff --git a/packages/vue-urql/src/useQuery.ts b/packages/vue-urql/src/useQuery.ts index 3883243623..f18990d30c 100644 --- a/packages/vue-urql/src/useQuery.ts +++ b/packages/vue-urql/src/useQuery.ts @@ -1,7 +1,5 @@ /* eslint-disable react-hooks/rules-of-hooks */ -import { DocumentNode } from 'graphql'; - import { WatchStopHandle, Ref, ref, watchEffect, reactive, isRef } from 'vue'; import { Subscription, Source, pipe, subscribe, onEnd } from 'wonka'; @@ -10,7 +8,7 @@ import { Client, AnyVariables, OperationResult, - TypedDocumentNode, + GraphQLRequestParams, CombinedError, OperationContext, RequestPolicy, @@ -22,22 +20,20 @@ import { import { useClient } from './useClient'; import { unwrapPossibleProxy } from './utils'; -type MaybeRef = T | Ref; - -export type UseQueryArgs = { - query: MaybeRef | DocumentNode | string>; - requestPolicy?: MaybeRef; - context?: MaybeRef>; - pause?: MaybeRef; -} & (V extends void - ? { - variables?: MaybeRef<{ [K in keyof V]: MaybeRef }>; - } - : V extends { [P in keyof V]: V[P] | null } - ? { variables?: MaybeRef<{ [K in keyof V]: MaybeRef }> } - : { - variables: MaybeRef<{ [K in keyof V]: MaybeRef }>; - }); +type MaybeRefObj = { + [K in keyof T]: T[K] | Ref; +}; + +export type UseQueryArgs< + Data = any, + Variables extends AnyVariables = AnyVariables +> = MaybeRefObj< + { + requestPolicy?: RequestPolicy; + context?: Partial; + pause?: boolean; + } & GraphQLRequestParams +>; export type QueryPartialState< T = any, diff --git a/packages/vue-urql/src/useSubscription.ts b/packages/vue-urql/src/useSubscription.ts index 6952599209..0ff30da575 100644 --- a/packages/vue-urql/src/useSubscription.ts +++ b/packages/vue-urql/src/useSubscription.ts @@ -1,15 +1,14 @@ /* eslint-disable react-hooks/rules-of-hooks */ -import { DocumentNode } from 'graphql'; import { Source, pipe, subscribe, onEnd } from 'wonka'; import { WatchStopHandle, Ref, ref, watchEffect, reactive, isRef } from 'vue'; import { Client, + GraphQLRequestParams, AnyVariables, OperationResult, - TypedDocumentNode, CombinedError, OperationContext, Operation, @@ -21,23 +20,17 @@ import { useClient } from './useClient'; import { unwrapPossibleProxy } from './utils'; type MaybeRef = T | Ref; +type MaybeRefObj = { [K in keyof T]: MaybeRef }; export type UseSubscriptionArgs< - T = any, - V extends AnyVariables = AnyVariables -> = { - query: MaybeRef | DocumentNode | string>; - pause?: MaybeRef; - context?: MaybeRef>; -} & (V extends void - ? { - variables?: MaybeRef<{ [K in keyof V]: MaybeRef }>; - } - : V extends { [P in keyof V]: V[P] | null } - ? { variables?: MaybeRef<{ [K in keyof V]: MaybeRef }> } - : { - variables: MaybeRef<{ [K in keyof V]: MaybeRef }>; - }); + Data = any, + Variables extends AnyVariables = AnyVariables +> = MaybeRefObj< + { + pause?: boolean; + context?: Partial; + } & GraphQLRequestParams +>; export type SubscriptionHandler = (prev: R | undefined, data: T) => R; export type SubscriptionHandlerArg = MaybeRef>; From dc59fb6b8d27dd2e632f6641c4a6d8a98ffdaceb Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 9 Mar 2023 04:47:31 +0000 Subject: [PATCH 3/3] Fix missing ref casts in @urql/vue --- packages/vue-urql/src/useQuery.ts | 29 ++++++++++++------------ packages/vue-urql/src/useSubscription.ts | 20 ++++++++-------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/vue-urql/src/useQuery.ts b/packages/vue-urql/src/useQuery.ts index f18990d30c..cc22fd5e62 100644 --- a/packages/vue-urql/src/useQuery.ts +++ b/packages/vue-urql/src/useQuery.ts @@ -20,20 +20,17 @@ import { import { useClient } from './useClient'; import { unwrapPossibleProxy } from './utils'; -type MaybeRefObj = { - [K in keyof T]: T[K] | Ref; -}; +type MaybeRef = T | Ref; +type MaybeRefObj = { [K in keyof T]: MaybeRef }; export type UseQueryArgs< Data = any, Variables extends AnyVariables = AnyVariables -> = MaybeRefObj< - { - requestPolicy?: RequestPolicy; - context?: Partial; - pause?: boolean; - } & GraphQLRequestParams ->; +> = { + requestPolicy?: MaybeRef; + context?: MaybeRef>; + pause?: MaybeRef; +} & MaybeRefObj>; export type QueryPartialState< T = any, @@ -88,7 +85,7 @@ export function callUseQuery( const request: Ref> = ref( createRequest( - args.query, + unwrapPossibleProxy(args.query as any), unwrapPossibleProxy(args.variables as V) ) as any ); @@ -98,7 +95,7 @@ export function callUseQuery( stops.push( watchEffect(() => { const newRequest = createRequest( - args.query, + unwrapPossibleProxy(args.query as any), unwrapPossibleProxy(args.variables as V) ); if (request.value.key !== newRequest.key) { @@ -111,8 +108,10 @@ export function callUseQuery( watchEffect(() => { source.value = !isPaused.value ? client.value.executeQuery(request.value, { - requestPolicy: args.requestPolicy, - ...args.context, + requestPolicy: unwrapPossibleProxy( + args.requestPolicy + ) as RequestPolicy, + ...unwrapPossibleProxy(args.context), }) : undefined; }, watchOptions) @@ -128,7 +127,7 @@ export function callUseQuery( isPaused, executeQuery(opts?: Partial): UseQueryResponse { const s = (source.value = client.value.executeQuery(request.value, { - requestPolicy: args.requestPolicy, + requestPolicy: unwrapPossibleProxy(args.requestPolicy) as RequestPolicy, ...args.context, ...opts, })); diff --git a/packages/vue-urql/src/useSubscription.ts b/packages/vue-urql/src/useSubscription.ts index 0ff30da575..0da7c4b7f9 100644 --- a/packages/vue-urql/src/useSubscription.ts +++ b/packages/vue-urql/src/useSubscription.ts @@ -19,18 +19,16 @@ import { import { useClient } from './useClient'; import { unwrapPossibleProxy } from './utils'; -type MaybeRef = T | Ref; +type MaybeRef = Exclude | Ref>; type MaybeRefObj = { [K in keyof T]: MaybeRef }; export type UseSubscriptionArgs< Data = any, Variables extends AnyVariables = AnyVariables -> = MaybeRefObj< - { - pause?: boolean; - context?: Partial; - } & GraphQLRequestParams ->; +> = { + pause?: MaybeRef; + context?: MaybeRef>; +} & MaybeRefObj>; export type SubscriptionHandler = (prev: R | undefined, data: T) => R; export type SubscriptionHandlerArg = MaybeRef>; @@ -100,7 +98,7 @@ export function callUseSubscription< const request: Ref> = ref( createRequest( - args.query, + unwrapPossibleProxy(args.query as any), unwrapPossibleProxy(args.variables as V) ) as any ); @@ -110,7 +108,7 @@ export function callUseSubscription< stops.push( watchEffect(() => { const newRequest = createRequest( - args.query, + unwrapPossibleProxy(args.query as any), unwrapPossibleProxy(args.variables as V) ); if (request.value.key !== newRequest.key) { @@ -123,7 +121,7 @@ export function callUseSubscription< watchEffect(() => { source.value = !isPaused.value ? client.value.executeSubscription(request.value, { - ...args.context, + ...(unwrapPossibleProxy(args.context) as Partial), }) : undefined; }, watchOptions) @@ -173,7 +171,7 @@ export function callUseSubscription< opts?: Partial ): UseSubscriptionState { source.value = client.value.executeSubscription(request.value, { - ...args.context, + ...unwrapPossibleProxy(args.context), ...opts, });